Webpack is both a joy and a worry, powerful but with a learning cost. Before exploring the WebPack plugin mechanism, one interesting thing to understand is that the WebPack plugin mechanism is the backbone of the entire WebPack tool, and webPack itself is built using this plugin mechanism. Therefore, after in-depth understanding of the webPack plug-in mechanism, and then carry out the relevant optimization of the project, it will be of great benefit.

To ensure real-time and accurate content, you can follow personal blogs.

Webpack plug-in

Take a look at the use of the WebPack plug-in in your project

const MyPlugin = require('myplugin')
const webpack = require('webpack') webpack({ ... .plugins: [new MyPlugin()]
  ...,
})
Copy the code

So what are the criteria for being a WebPack plug-in? In general, WebPack plug-ins have the following features:

  1. Separate JS modules, exposing corresponding functions

  2. The Apply method on the function prototype injects the Compiler object

  3. The corresponding WebPack event hook is mounted on the Compiler object

  4. The callback function of the event hook can retrieve the compiled compilation object or, if asynchronous, the corresponding callback

Let’s look at the code below:

function MyPlugin(options) {}
// 2. The Apply method on the function prototype injects the Compiler object
MyPlugin.prototype.apply = function(compiler) {
  The corresponding WebPack event hook 4 is mounted on the piler object. The callback function of the event hook retrieves the compiled compilation object
  compiler.plugin('emit', (compilation, callback) => { ... })}// 1. Separate JS modules, exposing corresponding functions
module.exports = MyPlugin
Copy the code

Now that the basic outline of the WebPack plug-in is outlined, there are a few questions,

  1. Question 1: Why is the apply method defined on a function prototype? readingThe source codeAfter the discovery of the source is passedplugin.apply()Of the plug-in.
const webpack = (options, callback) = >{... for (const plugin ofoptions.plugins) { plugin.apply(compiler); }... }Copy the code
  1. Question 2: What are compiler objects?

  2. Question 3: What are the event hooks on compiler objects?

  3. Question 4: What compilation objects can be retrieved from the event hooks’ callbacks?

These questions are also the clues of this article, let us explore one by one.

The compiler object

Compiler is the editor object of Webpack. When calling Webpack, the compiler object will be initialized automatically. The source code is as follows:

// webpack/lib/webpack.js
const Compiler = require("./Compiler")

const webpack = (options, callback) = >{... options =new WebpackOptionsDefaulter().process(options) // Initializes webPack configuration parameters
  let compiler = new Compiler(options.context)             // Initialize the compiler object, where options.context is process.cwd()
  compiler.options = options                               // Add initialization parameters to compiler
  new NodeEnvironmentPlugin().apply(compiler)              // Add Node environment-specific methods to compiler
  for (const plugin ofoptions.plugins) { plugin.apply(compiler); }... }Copy the code

Finally, the Compiler object contains all webPack configurable content, and we can get all webPack main environment related content from the Compiler object when developing the plug-in.

Compilation object

The compilation object represents a single release build and generation resource. When running WebPack, each time a file change is detected, a new build is created, resulting in a new set of build resources. A compiled object represents the current module resources, compile-generated resources, changing files, and state information that is being traced.

Each time webpack executes it, it calls Compiler.run () (where the source code is), and then tracks the compilation parameter passed in by onCompiled. You can see that the compilation comes from the constructor compilation.

// webpack/lib/Compiler.js
const Compilation = require("./Compilation");

newCompilation(params) {
  const compilation = new Compilation(this); . return compilation; }Copy the code

The Tapable library has to be mentioned

After introducing compiler objects and compilation objects, we must mention the Tapable library, which exposes all pub/sub methods associated with events. The function Compiler and function Compilation inherit from Tapable.

Event hooks

Event hooks are basically lifecycle functions similar to the MVVM framework that do special logical processing at specific stages. Understanding some common event hooks is a prerequisite for writing webPack plugins. Here are some common event hooks and what they do:

hook role parameter type
after-plugins After setting up a set of initialization plug-ins compiler sync
after-resolvers After setting up the resolvers compiler sync
run Before reading the record compiler async
compile Before creating a new compilation compilationParams sync
compilation Compilation created compilation sync
emit Before the resource is generated and output to the directory compilation async
after-emit After the resource is generated and output to the directory compilation async
done compile stats sync

Refer to the official documentation manual for its entirety, and browse the source code to see the definition of each event hook.

Brief Analysis of plug-in process

Using the emit hook as an example, let’s examine the plugin call source code:

compiler.plugin('emit', (compilation, callback) => {
  // Complete some logic before generating the resource and exporting it to the directory
})
Copy the code

The plugin function called here is derived from the tapable library mentioned above, and its final call stack points to hook.tapasync (), which acts like EventEmitter’s on. The source code is as follows:

// Tapable.jsoptions => { ... if(hook ! = =undefined) {
    const tapOpt = {
      name: options.fn.name || "unnamed compat plugin".stage: options.stage || 0
    };
    if(options.async)
      hook.tapAsync(tapOpt, options.fn); // Inject the callback function of the asynchronous hook in the plug-in
    else
      hook.tap(tapOpt, options.fn);
    return true; }};Copy the code

The callAsync method is used to trigger asynchronous events injected before. CallAsync is similar to EventEmitter emit.

this.hooks.emit.callAsync(compilation, err => {
	if (err) return callback(err);
	outputPath = compilation.getPath(this.outputPath);
	this.outputFileSystem.mkdirp(outputPath, emitFiles);
});
Copy the code

I won’t go into some of the details here, but two things I learned about reading the source code for larger projects,

  • Stick to one main clue and ignore the details. Otherwise it will waste a lot of time and be frustrating;

  • Combined with debugging tools to analyze, a lot of points without debugging tools is easy to lose;

Start a Webpack plug-in

It is not difficult to write your own WebPack plug-in based on the above analysis. The key is the idea. In order to count the effective use of each webpack in the project, we upgraded the code based on fork Webpack-Visualizer, the project address. The effect is as follows:

The plug-in core code is based on the EMIT hooks mentioned above, as well as the Compiler and compilation objects. The code is as follows:

class AnalyzeWebpackPlugin {
  constructor(opts = { filename: 'analyze.html'{})this.opts = opts
  }

  apply(compiler) {
    const self = this
    compiler.plugin("emit".function (compilation, callback) {
      let stats = compilation.getStats().toJson({ chunkModules: true }) // Get the status of each module
      let stringifiedStats = JSON.stringify(stats)
      // Server render
      let html = ` <! doctype html> <meta charset="UTF-8"> <title>AnalyzeWebpackPlugin</title> <style>${cssString}</style>
          <div id="App"></div>
          <script>window.stats = ${stringifiedStats}; </script> <script>${jsString}</script>
      `
      compilation.assets[`${self.opts.filename}`] = { // Generate file path
        source: (a)= > html,
        size: (a)= > html.length
      }
      callback()
    })
  }
}
Copy the code

The resources

Take a look at the real Webpack plug-in

Webpack website