Graphic webpack — implementation of plugin

Time:2021-9-22

Graphic webpack -- implementation of plugin

Pay attention to the official account “Kite holder”, reply“book”Get a lot of front-end learning materials and reply“Front end video”Get a lot of front-end teaching videos.

Extra points for interview!
Plugin is an important part of webpack ecosystem. Its purpose is to solve other things that cannot be realized by loader. It can be used to perform a wider range of tasks and bring great flexibility to webpack. The existing plugins can not fully meet all development needs, so customizing plugins that meet their own needs has become the only way to learn webpack. Next, several key technical points in plugin development will be described step by step and plugin will be implemented.

Graphic webpack -- implementation of plugin

1、 Webpack build process

Before implementing your own plugin, you need to understand the construction process of webpack (figure below). The construction process of webpack is similar to a production line (the event flow mechanism of webpack). When there is a specific opportunity to broadcast the corresponding events, the plug-in can listen to the occurrence of these events and do specific things at a specific time.

Graphic webpack -- implementation of plugin

The above figure shows the three stages of the webpack construction process and the events broadcast in each stage.

  1. Initialization phase: start construction; Then read and merge the parameters from the configuration file and shell statement to get the final parameters; Initialize the compiler object with the parameters obtained in the previous step and load all configured plug-ins.
  2. Starting from entey, each module serially calls the corresponding loader to translate the contents of the file, then finds the module that the module depends on, and recursively compiles to obtain the final translated contents of each module and their dependencies.
  3. According to the dependency between the entry and the module, the chunks containing multiple modules are assembled, and then each chunk is converted into a separate file and added to the output list. Finally, the contents of the required output file are written into the file system.

2、 Write plugin

There are four steps to write plugin:

  • Create a named JavaScript function
  • Define the apply method on its prototype
  • Attach event hooks to related events in the webpack build process to listen
  • After the hook function is triggered, use compiler, compilation and other related operations to achieve the desired effect

2.1 basic structure

class MyPlugin {
    constructor(options) {

    }

    apply(compiler) {
        console.log(compiler);
        compiler.hooks.emit.tap('MyPlugin', () => {
            console.log('plugin is used!!!');
        })
    }
}

module.exports = MyPlugin;

2.2 apply method

The apply method will be called once by the webpack compiler when the plug-in is installed. The apply method can receive a reference to a webpack compiler object so that the compiler object can be accessed in the callback function.

2.3 hook function

During the whole compilation process of webpack, a large number of hooks are exposed for internal / external plug-ins. After registering the hook, you can monitor the corresponding events in the whole webpack event stream. The core of these hook functions is tapable. The package exposes many hook classes, which are used to create the above hook functions. Common hooks mainly include the following:

Graphic webpack -- implementation of plugin

Hook functions exposed to compiler and compilation are built based on the above hooks.

Graphic webpack -- implementation of plugin

2.4 hook function registration method

Graphic webpack -- implementation of plugin

In the plugin, there are three hook registration methods: tap, tapasync and tappromise. The corresponding methods are used to register according to the type of hook function. Synchronous hook functions are registered with tap, and asynchronous hook functions are registered with tap, tapasync and tappromise.

  1. tap

Tap can be used to register synchronous hooks and asynchronous hooks.

compiler.hooks.compile.tap('MyPlugin', compilationParams => {
    Console.log ('touch the compile hook synchronously ')
});
  1. tapAsync

Tapasync can be used to register asynchronous hooks and notify webpack of the completion of asynchronous logic execution through callback.

compiler.hooks.run.tapAsync('MyPlugin', (compiler, callback) => {
    Console.log ('tapasync ');
    callback();
});
  1. tapPromise

Tappromise can be used to register an asynchronous hook, which tells webpack that the asynchronous logic has been executed by returning promise. (there are two implementation methods, one is to return the promse function, and the other is to use async).

//Mode 1
compiler.hooks.run.tapPromise('MyPlugin', (compiler) => {
    return new Promise(resolve => setTimeout(resolve, 1000)).then(() => {
        Console.log ('tappromise asynchronous')
    })
})

//Mode II
compiler.hooks.run.tapPromise('MyPlugin', async (compiler) => {
    await new Promise(resolve => setTimeout(resolve, 1000));
    Console.log ('tappromise async ')
})

2.5 compiler and compilation

Compiler and compilation are bridges between plugin and webpack, so it is important to understand their specific meanings, which are as follows:

  • The compiler object contains all configuration information of the webpack environment, including options, loaders, plugins, etc. This object is instantiated when webpack starts. It is globally unique and can be simply understood as a webpack instance.
  • The compilation object contains the current module resources, compilation generated resources, changed files, etc. When webpack runs in development mode, every time a file change is detected, a new compilation is

establish. The compilation object also provides many event callbacks for plug-ins to extend. Compiler objects can also be read through compilation.

3、 Custom hook function

Graphic webpack -- implementation of plugin

Are there only official hook functions? Certainly not. We can implement a hook function according to our own needs. The implementation of the hook function is mainly divided into the following steps:

  1. Call the tabable package and select the appropriate hook
  2. Mount the hook function defined by yourself to the compiler or compilation
  3. Register the hook function at the desired location
  4. Trigger the corresponding hook function at the time you need
//The code has the registered synchronous hook function and asynchronous hook function and their triggering process
const { SyncHook, AsyncSeriesHook } = require('tapable');

class MyHookPlugin {
    constructor() {}

    apply(compiler) {
        //Mount
        compiler.hooks.myHook = new SyncHook(['arg1', 'arg2']);
        compiler.hooks.myAsyncHook = new AsyncSeriesHook(['arg1', 'arg2']);

        //Register
        //Synchronization
        compiler.hooks.myHook.tap('myHook', (arg1, arg2) => {
            Console.log ('self defined hook function is triggered ', arg1, arg2);
        })

        //Asynchronous 1
        compiler.hooks.myAsyncHook.tapAsync('myHook', (arg1, arg2, callback) => {
            Console.log ('asynchronous hook tapasync ', arg1, arg2);
            callback();
        });
        //Asynchronous 2
        compiler.hooks.myAsyncHook.tapPromise('myHook', (arg1, arg2) => {
            return new Promise((resolve) => {
                resolve({arg1, arg2});
            }).then((context) => {
                Console.log ('asynchronous hook tappromise ', context)
            })
        });

        compiler.hooks.environment.tap('myHookPlugin', () => {
            //Trigger
            //Synchronization
            compiler.hooks.myHook.call(1, 2);
            //Asynchronous 1
            compiler.hooks.myAsyncHook.callAsync(1, 2, err => {
                Console.log ('trigger completed... Callasync ')
            })
            //Asynchronous 2
            compiler.hooks.myAsyncHook.promise(1, 2).then(err => {
                Console.log ('trigger complete... Promise ')
            })
        })
    }
}

module.exports = MyHookPlugin;

4、 Implement plugin

This section is the actual operation of plugin, which generates a resource list before generating resources to the output directory.

class MyPlugin {
    constructor(options) {

    }

    apply(compiler) {
        compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
            Console.log ('plugin is used!!! ', "emit event");
            const mainfest = {}
            for (const name of Object.keys(compilation.assets)) {
                //Compilation. Assets [name]. Size() gets the size of the output file; Compilation. Assets [name]. Source() get content
                mainfest[name] = compilation.assets[name].size();
                console.log(compilation.assets[name].source())
            }

            compilation.assets['mainfest.json'] = {
                source() {
                    return JSON.stringify(mainfest);
                },
                size() {
                    return this.source().length;
                }
            };

            callback();
        });
    }
}

module.exports = MyPlugin;

Note: This article only plays a role in attracting jade. I hope you can give me more advice.

Relevant sections < br / >
Graphic webpack — Fundamentals<br/>
Graphic webpack — Optimization<br/>
Graphic webpack — implementing loader

Welcome to the official account (reply to “Webpack”, get the PDF version of Webpack, reply to webpack05, get the mind map of this section, reply to “book”, acquire a large amount of front-end learning materials, reply “front end video” and get a lot of front end teaching frequency).
Graphic webpack -- implementation of plugin