Webpack not suitable for multi-page applications? Gulp is the soul of Webpack, and it is the soul of Webpack. Gulp is the soul of Webpack. Get out of your comfort zone

Why is WebPack not suitable for multi-page applications

The main reason is that there are no plugins that can handle multiple HTML templates:

  • React is a natural blend of WebPack, because react takes over the View layer of web applications. As JS, it does what HTML should do. HTML only needs to provide a container to import the generated bundle.js file
  • For single-page applications, the situation is similar in that JS does a lot of the page rendering. If the only entry HTML needs to be handled, you can use the html-webpack-plugin, which generates an HTML, Inject all the generated JS files (and all CSS files if the extract-text-webpack-plugin is used), and for the contents of the body, pass in the template and the loader that parses the template
  • For multi-page applications, things get a little more complicated. Creating a separate HTML-webpack-plugin for different pages, passing in different templates, and filtering HTML template files with parameters Chunks and excludeChunks is one way, but not a good one. Developers have too much to distract themselves from developing HTML files, which is not a hallmark of a good framework. This is where the curve saves the country. Webpack does not manage the HTML template, leaving it to GulP to parse and move the HTML. This seemingly happy solution has no choice. For external links in HTML (JS, CSS, and images), if we package the result with a hash value, we have to do a very time-consuming substitution of traversal judgment in order to reference the HTML correctly.

If not, just write one

To handle multi-page applications, we need a plug-in like this:

  1. Configure a templatePath (templatePath) and the plug-in will automatically find all the HTML templates in that directory, ready to be parsed
  2. You can configure a template loader to parse template files. You can configure a template loader to parse template files. The configuration in module.loaders is also valid
  3. You can configure the templateSuffix suffix to filter non-template files in the template path
  4. You can configure the files to be filtered (ignore). Some common templates, such as header and footer, are used for reference only and do not need to be output as separate HTML files, which also need to be filtered
  5. You can configure the output path of HTML files.
  6. [name]. CSS/JS ## #. Name is the key value in the entry configuration item, JS is used for the JS file, CSS is used for the CSS file, and no replacement is performed if the match fails

    / / CSS reference<link rel="stylesheet" type="text/css" href="##entry.demo.css##">/ / js references<script type="text/javascript" src='##entry.demo.js##'></script>Copy the code

How to write this plug-in

First, check the documentation

Not many people want to write a Webpack-plugin, Because why? When you open the official webpack document how to Write a Plugin, in the first paragraph, there is a sentence that stands out:

Plugins expose the full potential of the Webpack engine to third-party developers. Using staged build callbacks, developers can introduce their own behaviors into the Webpack build process. Building plugins is a bit more advanced than building loaders, because you’ll need to understand some of the Webpack low-level internals to hook into them. Be prepared to read some source code!

Yes you have to be ready to read the source code, see here, many students have lost the courage to continue! However, I still need to read this document. If English is not good, there are translations and various analysis materials available on the Internet. This document does not contain much information, but I think the most important concepts are as follows:

  • There are two important objects, compiler and compilation, on which all plug-in operations are based, and the documentation recommends that you read their source code
  • Modules, each resource file is compiled into a module. Module. FileDependencies record other modules that the module depends on, for example: Index.js introduces the index.css module by requiring (‘./index.css’), so the inde.js module’s dependencies include the index.css module
  • Compilation. chunks is each configuration item of entry, chunk.modules, entry modules that each configuration item contains and modules that the module depends on, chunk.files, the last output file of each configuration item, The values here can be obtained from compilation.assets
  • Compilation. Assets, the final output file of the whole packaging process

However, after reading the entire document, or can not write to meet our needs of the plug-in, it seems that we must go out of their comfort zone of thinking, to read the source code, the key to reading other people’s source code is to understand other people’s design ideas

Second, webpack design idea

To illustrate the design concept of Webpack, it is necessary to compare the gulP process to the gulP process:





Gulp process

Webpack processing process:





Webpack process

Gulp processing process is easy to understand, one task after another, the data flow is transferred down, the next task is processed, the whole process is process-oriented

Webpack treats resources as modules, handles dependencies between modules, and consolidates outputs. Unlike the scalability of gulp’s serial task chain, webPack has a built-in life cycle. Webpack is much more than that. Webpack core uses Tapable plug-in architecture to realize the publishing and subscription processing of events. For each link of the inherent life cycle, corresponding events will be thrown to provide plug-in cuts. This is a faceted idea, each plug-in can cut into multiple event points, and the processing logic behind each event point (such as event2: Plugin1.doevent2, plugin2.doevent2), can run independently, but also can have certain dependencies, there are too many plug-ins, the figure can not fully express the special, in the plug-in can also create a sub-compilation process, it can be compiled and analyzed like the main process.

Gulp just defines a pipeline of streams, but WebPack already has a complete process for analyzing, compiling, and packaging the output, as well as a plug-in mechanism that allows you to access all aspects of the process. Besides having a higher barrier to write plug-ins, WebPack has the same flexibility as Gulp

I will summarize and analyze what events webpack throws in the specific link. Please pay attention to them

OK, understand the whole working process, and then read the source code, do not have to be so confused

3, htML-Webpack-plugin analysis

If you wait until every detail of Webpack is clear (it is necessary to read the excellent code when you are free, and benefit a lot), then start to write this plug-in, it is too hard, the most accurate and malicious way is to find a close plug-in, first read it, and then transform it. Yes, the plugin is the HTML-webpack-plugin. The process is more tortuous, but the results are considerable:

  • Plugin important parameters (parameters are not mandatory, and there are many parameters, we are analyzing its operation mechanism, not how to use, not enumerating) :
    • Filename: indicates the output filename. The default value is index.html
    • Ejs and the default loader.js are used for template parsing. This template is an empty HTML template.
  • The plug-in listens for the make event, that is, when the main process parses the entry file and prepares to generate the module file, the plug-in creates a child compilation process that specifically compiles the template and prints it to filename. This subcompilation process is processed asynchronously relative to the main process, so the main process creates a promise for a period of time to receive notification that its processing is complete
  • The plugin also listens for emit events, which means that the compilation. Assets file is about to be output, and the plugin has one last chance to change the output
    • You still have to wait for the subcompilation process to finish
      Promise.resolve()
      // Favicon
      .then(function (a) {
        if (self.options.favicon) {
          return self.addFileToAssets(self.options.favicon, compilation)
            .then(function (faviconBasename) {
              var publicPath = compilation.options.output.publicPath || ' ';
              if (publicPath && publicPath.substr(- 1)! = ='/') {
                publicPath += '/'; } assets.favicon = publicPath + faviconBasename; }); }})// Wait for the compilation to finish
      .then(function (a) {
        // This step is to wait for compilationPromise
        return compilationPromise;
      })
      .then(function (compiledTemplate) {
        // Allow to use a custom function / string instead
        if (self.options.templateContent) {
          return self.options.templateContent;
        }
        // Once everything is compiled evaluate the html factory
        // and replace it with its content
        return self.evaluateCompilationResult(compilation, compiledTemplate);
      })Copy the code
    • The compiled results are then further processed by injecting js and CSS files (chunks). Of course, whether or not to inject and which files to inject can be controlled by parameters
    • Finally, the result of the processing is put into the compilation.assets, and the HTML file is output

Many of these steps also throw events for other plug-ins to listen for, such as:

    // Allow plugins to make changes to the assets before invoking the template
      // This only makes sense to use if `inject` is `false'// An htmL-webpack-plugin-before-html event was thrown in the immediate HTML file generation phase.then(function (compilationResult) {
        return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-generation', {
          assets: assets,
          outputName: self.childCompilationOutputName,
          plugin: self
        })
        .then(function () {
          return compilationResult;
        });
      })Copy the code

Fourth, the road of transformation

Through the analysis of the last section, we can start the long road of transformation

  1. First of all, what we need is a can handle multiple templates, so we should be introduced into the template path, if there is a need to also can be introduced into the template suffix, as well as the need to rule out the file, the template of path filtering, so the first thing that we want to write a method iterates through all template file directory, if introduced into the output relative to the directory path, You also need to do something with the file name
     // suffix filter
     if(! suffix || filePath.indexOf(suffix) >=0) {
         var key = filePath.substring(dirname.length + 1);
         / / ignore filter
         if(! ignore || ignore.indexOf(key) < 0) {
             // Generate file name processing
             if(! suffix &&key.indexOf('.html') > =0) { .html is filtered by default
                 key = key.substring(0.key.indexOf('.html'));
             } else if(!!!!! suffix) {key = key.substring(0.key.indexOf(suffix));
             }
             if(!!!!! outputPath) {key = outputPath + '/' + key;
             }
             //fileJson is equivalent to entry configuration in webpack.config.js,
             // To pass the child compiler process to compile
             fileJson[key] = filePath; }}Copy the code
  2. Similarly, we need a subcompiler process to parse the template file based on the loader we passed in. The previous subcompiler process only needs one file, so we need to modify it a little bit

     //html-webpack-plugin
     childCompiler.apply(
         new NodeTemplatePlugin(outputOptions),
         new NodeTargetPlugin(),
         new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT'.'var'),
         // The plugin passes in an entry file to process
         new SingleEntryPlugin(this.context, template), 
         new LoaderTargetPlugin('node'));//muti-html-webpack-plugin
     childCompiler.apply(
         new NodeTemplatePlugin(outputOptions),
         new NodeTargetPlugin(),
         new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT'.'var'),
         // Comment out the previous processing plug-in
         // new SingleEntryPlugin(this.context, template),
         new LoaderTargetPlugin('node'));// Define a method to add plug-ins
        function itemToPlugin(item, name) {
           if(Array.isArray(item)) // If the entry is an array, use multiEntry
             return new MultiEntryPlugin(context, item, name);
           else
             return new SingleEntryPlugin(context, item, name);
         }
    
       // Iterate through the template entries we passed in
       Object.keys(template).forEach(function(name) {
         childCompiler.apply(itemToPlugin(template[name], name));
       });Copy the code
  3. The main process emit also waits for the result of the sub-compilation process and matches the result of each template##entry.[name].js/css##Is replaced with the corresponding chunks file
  4. Add the result to assets, ready for output

The latter

Plugins: mut-html-webpack-plugin comes with a nunjucks template parser :compile-nunjucks-loader You can write your own plugins as much as you want

More exciting, invite your attention: