zishu's blog

zishu's blog

一个热爱生活的博主。https://zishu.me

Core Concepts and Build Process of Webpack

1. Core Concepts of webpack#

  1. entry: An entry point for an executable module or library. Defines the entry file after packaging.
  2. output: Indicates how and where webpack should output the files.
    path: The absolute path where the packaged files are stored
    publicPath: The access path when the website is running
    filename: The name of the packaged file
  3. module: In webpack, everything is a module, and a module corresponds to a file. webpack starts from the configured entry to recursively find all dependent modules.
  4. chunk: A chunk is composed of multiple modules. Executable modules and their dependent modules can be combined into a chunk, which is the essence of packaging.
  5. loader: A module transformer used to convert the original content of a module into new content as needed. For example: converting es6 to es5, scss to css, etc.
  6. plugin: Plugins that extend the functionality of webpack. They add extension hooks at various lifecycle nodes of the webpack build to add features.

2. webpack Build Process#

  1. Initialize Parameters: Parse webpack configuration parameters, merge parameters passed from the shell and those in the webpack.config.js file to form the final configuration result.
  2. Start Compilation: The parameters obtained in the previous step initialize the compiler object, register all configured plugins, and the plugins listen to the event nodes of the webpack build lifecycle, responding accordingly and executing the run method of the object to start the compilation.
  3. Determine Entry: The configured entry starts parsing the file to build the AST syntax tree, finding dependencies and recursively going deeper.
  4. Compile Modules: Based on file types and loader configurations, all configured loaders are called to transform the files, then find the dependent modules of that module, and recursively process this step until all entry-dependent files have been handled.
  5. Complete Module Compilation and Output: After recursion, the results of each file are obtained, including each module and their dependencies, generating code chunks based on the entry configuration.
  6. Output Completion: Output all chunks to the file system.

3. Common Loaders#

  1. babel-loader: Converts es6 to es5;
  2. css-loader: Loads css, supports modularization, compression, file imports, etc.;
  3. style-loader: Injects css code into js, loading css through DOM manipulation;
  4. eslint-loader: Checks js code using Eslint;
  5. image-loader: Loads and compresses images;
  6. file-loader: Outputs files to a folder, referenced in code via relative URLs;
  7. url-loader: Similar to file-loader, can inject file content into code as base64 when the file is small.
  8. source-map-loader: Loads additional source map files for easier debugging.

4. Business Scenarios and Corresponding Solutions#

1. Single Page Application#

A single page application needs to configure an entry to specify the execution entry. The WebPlugin in web-webpack-plugin can automatically complete this task: webpack will generate a chunk containing all the dependency files of this entry, but an HTML file is still needed to load the js generated by the chunk. If css is also extracted, the extracted css needs to be included in the HTML file.

A simple example of a webpack configuration file:

const { WebPlugin } = require('web-webpack-plugin');
module.exports = {
  entry: {
    app: './src/doc/index.js',
    home: './src/doc/home.js'
  },
  plugins: [
    // A WebPlugin corresponds to generating an HTML file
    new WebPlugin({
      // The name of the output HTML file
      filename: 'index.html',
      // The `entry` that this HTML depends on
      requires: ['app','home'],
    }),
  ],
};

Note: require: ['app', 'home'] specifies which entries this HTML depends on; the js and css generated by the entry will be automatically injected into the HTML.

It also supports configuring the way these resources are injected, supporting the following properties:

  1. _dist: Resources that are only included in the production environment;
  2. _dev: Resources that are only included in the development environment;
  3. _inline: Inject the content of the resource into the HTML;
  4. _ie: Resources that are only needed for IE browsers.

These properties can be configured in js, as shown in a simple example:

new WebPlugin({
    filename: 'index.html',
    requires: {
         app:{
              _dist:true,
              _inline:false,
         }
    },
}),

These properties can also be set in templates, which allows for flexible control over resource injection points.

new WebPlugin({
      filename: 'index.html',
      template: './template.html',
}),
<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="app?_inline">
    <script src="ie-polyfill?_ie"></script>
</head>
<body>
<div id="react-body"></div>
<script src="app"></script>
</body>
</html>

The WebPlugin draws on the ideas of fis3, filling in the missing functionality of webpack for HTML entry. For more information on WebPlugin's features, see the documentation.

2. A Project Managing Multiple Single Pages#

A project may contain multiple single page applications. Although multiple single page applications can be combined into one, doing so would cause parts that the user does not access to load as well, especially if there are many single page applications in the project. Configuring an entry and WebPlugin for each single page application? If a new one is added, the webpack configuration needs to be updated, which can be cumbersome. At this point, the AutoWebPlugin method in the web-webpack-plugin can solve these problems.

module.exports = {
    plugins: [
        // The entry directory for all pages
        new AutoWebPlugin('./src/'),
    ]
};

Analysis:

  1. AutoWebPlugin will treat each folder under the ./src/ directory as an entry for a single page, automatically configuring a WebPlugin for all page entries to output the corresponding HTML.
  2. To add a new page, simply create a new folder under ./src/ containing the code dependencies for that single page application, and AutoWebPlugin will automatically generate an HTML file named after the folder.

3. Code Splitting Optimization#

Good code splitting greatly enhances the browser's first screen performance.

Most Common React System:

  1. First, extract the basic libraries react, react-dom, redux, and react-redux into a separate file instead of packaging them together with other files. The benefit of this approach is that as long as you do not upgrade their versions, this file will never be refreshed. If you package these basic libraries with business code in one file, any change to the business code will cause the file's hash value to change, leading to cache invalidation and the browser re-downloading the code containing the basic libraries. Therefore, package the basic libraries into a separate file.
// vender.js file extracts basic libraries into a separate file to prevent them from being refreshed with business code
// Third-party libraries that all pages depend on
// React basics
import 'react';
import 'react-dom';
import 'react-redux';
// Redux basics
import 'redux';
import 'redux-thunk';
// webpack configuration
{
  entry: {
    vendor: './path/to/vendor.js',
  },
}
  1. The CommonsChunkPlugin can extract code that is shared among multiple chunks into a separate chunk. In scenarios where the application has multiple pages, extracting all shared code reduces the code for each individual page, allowing shared code to be loaded once and reused when switching between different pages. Thus, the CommonsChunkPlugin can extract shared code from multiple chunks into a separate chunk.

4. Building Server-Side Rendering#

Server-side rendering code needs to run in a nodejs environment, which differs from the browser in that server-side rendering code must use the commonjs specification and should not include files other than js, such as css.

The webpack configuration is as follows:

module.exports = {
  target: 'node',
  entry: {
    'server_render': './src/server_render',
  },
  output: {
    filename: './dist/server/[name].js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
      {
        test: /\.(scss|css|pdf)$/,
        loader: 'ignore-loader',
      },
    ]
  },
};

Analysis:

  1. target: 'node' indicates that the built code is intended to run in a node environment.
  2. libraryTarget: 'commonjs2' specifies that the output code should follow the commonjs specification.
  3. {test: /\.(scss|css|pdf)$/,loader: 'ignore-loader'} is to prevent files that cannot be executed in node or are not needed for server-side rendering from being packaged.

5. Migrating from fis3 to webpack#

fis3 and webpack have many similarities and differences. Similarities: both use the commonjs specification; differences: the way to import non-js resources like css.

fis3 uses @require './index.scss', while webpack uses require('./index.scss').

If you want to smoothly migrate from fis3 to webpack, you can use comment-require-loader.

For example, if you want to use a module that adopted the fis3 method in webpack:

loaders:[{
     test: /\.js$/,
     loaders: ['comment-require-loader'],
     include: [path.resolve(__dirname, 'node_modules/imui'),]
}]

5. Custom webpack Extensions#

If you cannot find a solution for your application scenario in the community, you will need to write your own loader or plugin.

Before writing a custom webpack extension, you need to clarify whether you want to create a loader or a plugin. You can determine this as follows:

If your extension is meant to transform individual files, then write a loader; everything else is a plugin.

Transforming files can include:

  1. babel-loader converting es6 to es5;
  2. file-loader replacing files with corresponding URLs;
  3. raw-loader injecting the content of text files into code.

1. Writing a webpack Loader#

Writing a loader is very simple, taking comment-require-loader as an example:

module.exports = function (content) {
    return replace(content);
};

The entry of the loader needs to export a function, and this function's job is to transform the content of a file.

The function receives a parameter content, which is the string representation of a file's content before transformation, and needs to return a new string representation as the result after transformation. All files imported through modularization will go through the loader. From this, it can be seen that loaders can only handle individual files and cannot process code blocks. You can refer to the official documentation.

2. Writing a webpack Plugin#

Plugins have a wide range of application scenarios, so they are slightly more complex. Taking end-webpack-plugin as an example:

class EndWebpackPlugin {

    constructor(doneCallback, failCallback) {
        this.doneCallback = doneCallback;
        this.failCallback = failCallback;
    }

    apply(compiler) {
        // Listen to events in the webpack lifecycle and handle them accordingly
        compiler.plugin('done', (stats) => {
            this.doneCallback(stats);
        });
        compiler.plugin('failed', (err) => {
            this.failCallback(err);
        });
    }
}

module.exports = EndWebpackPlugin;

The entry of the loader needs to export a class. When new EndWebpackPlugin() is called, the parameters required by this plugin are passed through the constructor. When webpack starts, it first instantiates the plugin and then calls the plugin's apply method. The plugin listens to events in the webpack lifecycle in the apply function and handles them accordingly.

Two Core Concepts of webpack Plugins:

  1. compiler: There is only one Compiler from the start to the exit of webpack, which holds the webpack configuration.
  2. compilation: Due to webpack's automatic compilation mechanism that listens for file changes, compilation represents a single compilation.

Both Compiler and Compilation broadcast a series of events. There are many events in the webpack lifecycle.

The above is just a simple demo; for more complex examples, refer to how to write a plugin or check web-webpack-plugin.

Reference article: https://www.cnblogs.com/chengxs/p/11022842.html

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.