preface

Go deep into Wepcak, although Vite is very popular indeed very fragrant, but Webpack still dominates in the enterprise, I don’t know later, it is very necessary to learn in-depth, mainly introduce loader/plugin and principle analysis, and those configurations only introduce some commonly used, I hope you can learn to read documents, I can’t read Chinese or English, I will learn later

so

A programmer’s career lasts about ten years, a tenth of a human lifespan. The front-end project is just a part of your life and work, but you are all of it. You are his soul. Stop playing games for long hours and playing games at work. Study more to accompany your project in the most perfect state!

The body of the

Article bottom small egg, please step by step to see the past!!

knowledge

  • Webpack front base
  • Loader mechanism (one by hand)
  • The Plugin mechanism
  • Small egg (introduce part of Webpack principle analysis)

Webpack Pre-basics (Configuration)

const path = require('path');

module.exports = {
  mode: "production".// "production" | "development" | "none"
  // Chosen mode tells webpack to use its built-in optimizations accordingly.

  entry: "./app/entry".// string | object | array
  // Here the application starts executing
  // Webpack starts to pack

  output: {
    // How does WebPack output the resulting options

    path: path.resolve(__dirname, "dist"), // string
    // The destination path of all output files
    // Must be an absolute path (using the Path module of Node.js)

    filename: "bundle.js".// string
    // File name template for Entry chunk (exit chunk?)

    publicPath: "/assets/".// string
    // Outputs the directory of parsed files, the URL relative to the HTML page

    library: "MyLibrary".// string,
    // Name of exported libraries

    libraryTarget: "umd".// Generic module definition
    // Exported library types

    /* Advanced output configuration (click show) */
  },

  module: {
    // About module configuration

    rules: [
      // Module rules (configures loader, parser, etc.)

      {
        test: /\.jsx? $/,
        include: [
          path.resolve(__dirname, "app")].exclude: [
          path.resolve(__dirname, "app/demo-files")].// Here is the matching condition, each option accepts a regular expression or string
        // Test and include have the same function in that they must match options
        // exclude is a mandatory mismatch option (prior to test and include)
        // Best practices:
        // - Use regular expressions only for test and file name matches
        // - Use an absolute path array in include and exclude
        // - Avoid exclude in favor of include

        issuer: { test, include, exclude },
        // Issuer condition (import source)

        enforce: "pre".enforce: "post".// flag that these rules apply, even if rules override (advanced option)

        loader: "babel-loader".// The loader that should be applied, which resolves relative to context
        For clarity, the '-loader' suffix is no longer optional in WebPack 2
        // View the WebPack 1 upgrade guide.

        options: {
          presets: ["es2015"]},// Loader is optional
      },

      {
        test: /\.html$/,
        test: "\.html$"

        use: [
          // Apply multiple loaders and options
          "htmllint-loader",
          {
            loader: "html-loader".options: {
              / *... * /}}]}, {oneOf: [ /* rules */]},// Use only one of these nested rules

      { rules: [ /* rules */]},// Use all these nested rules (merge available conditions)

      { resource: { and: [ / * * /]}},// Matches only if all conditions match

      { resource: { or: [ / * * /]}}, {resource: [ / * * /]},// Match any condition (default array)

      { resource: { not: / * * /}}// Match if condition does not match]./* Advanced module configuration (click show) */
  },

  resolve: {
    // Parse the options requested by the module
    // (not applicable to loader parsing)

    modules: [
      "node_modules",
      path.resolve(__dirname, "app")].// The directory used to find modules

    extensions: [".js".".json".".jsx".".css"].// The extension to use

    alias: {
      // List of module aliases

      "module": "new-module".// Alias: "module" -> "new-module" and "module/path/file" -> "new-module/path/file"

      "only-module$": "new-module".// Alias "only-module" -> "new-module", but does not match "only-module/path/file" -> "new-module/path/file"

      "module": path.resolve(__dirname, "app/third/module.js"),
      // Alias "module" -> "./app/third/module.js" and "module/file" will cause an error
      // Module alias imported relative to the current context}},devtool: "source-map".// enum
  // Enhance debugging by adding meta info to browser devTools
  // The 'source-map' that sacrifices build speed is the most detailed.

  devServer: {
    proxy: { // proxy URLs to backend development server
      '/api': 'http://localhost:3000'
    },
    contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
    compress: true.// enable gzip compression
    historyApiFallback: true.// true for index.html upon 404, object for multiple paths
    hot: true.// hot module replacement. Depends on HotModuleReplacementPlugin
    https: false.// true for self-signed, object for cert authority
    noInfo: true.// only errors & warns on hot reload
    // ...
  },

  plugins: [
    // ...].// List of add-ons


  /* Advanced configuration (click to show) */
}
Copy the code

The above content advanced CV operation from Webpack official website only post common configuration! This is not the main body, but the writing part

  • Entry: Entry, the first step in the build that Webpack performs will start with entry, which can be abstracted into input.
  • Module: Module, in Webpack everything is a module, a module corresponds to a file. Webpack recursively finds all dependent modules starting from the configured entry.
  • Chunk: a chunk is a code block. A chunk is composed of multiple modules for code merging and splitting.
  • Loader: module converter, used to convert the original content of a module into new content as required.
  • Plugin: The extension plug-in broadcasts corresponding events at specific times in the Webpack construction process. The plug-in can listen for these events and do corresponding things at specific times.

You need to know the basics before you can take the next step

Loader mechanism (one by hand)

Loader is a function that retrieves the source code of your entry file.

Implementing an image loader is not as difficult as you might think

  1. Get the buffer of the image
  2. Switch to base64 / write buffer to generate images

Began to try

// webpack.config.js
 module: {
        rules: [{test: /\.(png)|(jpg)|(gif)$/, use: [{
                    loader: "./loaders/img-loader.js".options: {
                        limit: 3000.> 3000 bytes use image, up to 3000 bytes use base64
                        filename: "img-[contenthash:5].[ext]"}}]}Copy the code

Gets module configuration items

Obtain the options passed in by the user from Loader using the loader-utils getOptions method:

var loaderUtil = require("loader-utils")

function loader(buffer) { // The buffer is given
    console.log("File data size :(bytes)", buffer.byteLength);
    var { limit = 1000, filename = "[contenthash].[ext]" } = loaderUtil.getOptions(this);
    if (buffer.byteLength >= limit) {
        var content = getFilePath.call(this, buffer, filename);
    }
    else{
        var content = getBase64(buffer)
    }
    return `module.exports = \`${content}\ ` `;
}

loader.raw = true; 
// Tells Webpack whether the Loader needs binary data via the exports.raw property

module.exports = loader;

// Get the base 64 format
function getBase64(buffer) {
    return "data:image/png; base64," + buffer.toString("base64");
}
// Build the image generation path
function getFilePath(buffer, name) {
    var filename = loaderUtil.interpolateName(this, name, {
        content: buffer
    });
    this.emitFile(filename, buffer);
    return filename;
}
Copy the code

The above file is written through this.emitfile

Synchronous and asynchronous

Loaders are synchronous and asynchronous. The preceding loaders are synchronous loaders because their conversion processes are synchronous and the conversion results are returned after completion. However, there are some scenarios where the transformation step can only be done asynchronously, such as when you need to make a network request to get the result, which can block the entire build and make it very slow if you do it synchronously.

module.exports = function(source) {
  // tell Webpack that the conversion is asynchronous and Loader will callback the result in callback
  var callback = this.async();
  someAsyncOperation(source, function(err, result, sourceMaps, ast) {
    // Callback returns the result of asynchronous execution
    callback(err, result, sourceMaps, ast);
  });
};
Copy the code

Cache acceleration

In some cases, some transformations are computationally expensive and time-consuming, and if the transformations are repeated with each build, the build will be slow. For this reason, Webpack caches all Loader processing results by default, which means that the corresponding Loader is not re-invoked to perform the conversion operation if the file to be processed or other dependent files have not changed.

If you want Webpack not to cache the Loader’s results, you can do this:

module.exports = function(source) {
  // Disable the caching function of the Loader
  this.cacheable(false);
  return source;
};
Copy the code

Know Webpack core loader again to introduce plugin

The Plugin mechanism

Plugin can do more work than Loader, more complex, its essence is a Class Class

Plugins, the basic structure of plug-ins, are objects that can be instantiated using their own stereotype method Apply. Apply is executed only once when the installed plug-in is installed by the Webpack compiler. The Apply method passes a reference to the WebPCK Compiler to access the compiler callback.

class HelloPlugin {
  // Get the configuration passed in by the user to the plug-in in the constructor
  constructor(options) {
    // ...
  }
  // Webpack calls the Apply method of the HelloPlugin instance and passes the Compiler object to the plug-in instance
  apply(compiler) {
    // Insert hook functions in the EMIT phase to process additional logic at specific times
    compiler.hooks.emit.tap('HelloPlugin'.compilation= > {
      // The Webpack callback function can be called after the functional flow is complete
    });
    If the event is asynchronous, it takes two parameters. The second parameter is the callback function, which is called to notify Webpack when the plug-in process has completed the task before moving on to the next process
    compiler.plugin('emit'.function(compilation, callback) {
      // Support processing logic
      // Execute callback to notify Webpack
      // If callback is not executed, the running process will be stuck at this pointcallback(); }); }}module.exports = HelloPlugin;
Copy the code

To use a plug-in, simply place an instance of it in the Plugins array configuration of Webpack:

const HelloPlugin = require('./hello-plugin.js');
module.exports = {
  plugins: [new HelloPlugin({ options: true})]};Copy the code

Let’s first analyze how the Webpack Plugin works:

  1. New HelloPlugin(options) initializes a HelloPlugin to fetch an example
  2. After initializing the Compiler object, call HelloPlugin.apply(Compiler) to pass the compiler object to the plug-in instance
  3. After obtaining the Compiler object, the plug-in instance can listen to events broadcast by Webpack through compiler.plugin(event name, callback function) and manipulate Webpack through compiler object

You can invoke the Compiler hooks during the Apply phase

Webpack’s Hoosk hooks are actually registered directly at various stages using Tapable, so let’s take the next step

Small egg (introduce part of Webpack principle analysis)

Webpack is essentially a packaged build tool, so let’s think about what it does for us.

  1. readwebpack.config.jsProfile, find the entry
  2. Get source code analysis abstract syntax tree in entry file (Babel implementation)
  3. The analysis process statically analyzes the code execution context and usage, marking whetherTree Shaking
  4. The core loader and plugin execute functions during reading configuration,tapableInjection hook function
  5. The output is in the exit directory of the configuration file

In fact, we have a simple analysis, is also very easy to understand

Compiler class Compiler {constructor(options) {// Webpack configuration const {entry, output} = options; // entry this. Entry = entry; // this. Output = output; // module this.modules = []; } // Build start run() {//... } // generate bundle generate() {//... }}Copy the code

Analyze the source code abstract syntax tree using the @babel/ Parser and @babel/traverse libraries to find the template dependencies used

The build process

The whole process can be roughly divided into three steps

Initialize the

At this stage, Webpack will integrate CLI parameters, configuration files and default configurations to form a final configuration object (completed by relying on yargs, a third-party library).

The initial phase is used to produce a final configuration

compile


Create the chunk

Chunk: Webpack’s concept of an internal build process, a chunk, which represents a collective name for all dependencies found through a portal

Create a chunk based on the entry module (default./ SRC /index.js)

Each chunk has at least two attributes:

  • Name: The default value is main
  • Id: unique id. Development environment and name are the same. Production environment is a number starting from 0

Build all dependent modules

AST online test tool: astExplorer.net/

brief

Produce the chunk assets

After the second step is complete, a list of modules is generated in chunk, which contains the module ID and the converted code of the module

Next, Webpack will generate a list of assets for chunk based on the configuration. The list of assets can be understood as the file name and content to be generated into the final file

The Chunk hash is a hash string generated based on the contents of all chunk assets

Hash: An algorithm with many classes that converts a string of arbitrary length into a string of fixed length, ensuring that the original content remains unchanged and the resulting hash string remains unchanged

brief


Merge the chunk assets

Merge assets of multiple chunks together to generate a hash

The output

This step is very simple, webPack will use the FS module in Node (file processing module), from the compiled total assets, to generate the corresponding file.

The total process

Design term

  1. A module in a Webpack can be a file of any content, not just JS
  2. Chunk: a chunk of webpack building blocks. A chunk contains multiple modules that are extracted from the entry module through dependency analysis
  3. Bundle: After chunk builds the module, a list of chunk resources will be generated. Each item in the list is a bundle, which can be considered as the final generated file
  4. Hash: The hash value generated by combining all the contents of the final resource list
  5. Chunkhash: Hash value jointly generated by the contents of the resource list generated by chunk
  6. Chunkname: indicates the name of chunk. If this parameter is not specified, main is used
  7. Id: the unique id of chunk. If the chunk is built in a development environment, it is the same as chunkName. If the build is in production, use a number starting from 0

Tapable is essentially a small javascript library with a publish-subscribe model inside. EventEmitter similar to Node.

Webpack is essentially an event flow mechanism, and its workflow is to chain plug-ins together, and Tapable is at the heart of it all, The core Compiler in Webpack and the Compilation that creates bundles are subclasses of Tapable, and the internal lifecycle of an instance is implemented through the Tapable library’s hook classes.

conclusion

Webpack is essentially a packaged build tool for the event flow mechanism, a process from reading the configuration to analyzing the syntax tree to register the event flow output file. The core loader is also a function that can access the source code, while plugin is a class that can access the entire event lifecycle. By this point you should have a better understanding of WebPack. This article skips many of the details and introduces the core knowledge, so you’ll have to read the configuration documentation more!

The articles

Javascript runtime context, precompilation

How does a browser complete a page? (Loading)