What is the use of webPack plug-ins

Webpack plug-ins are used to handle functions that cannot be completed by Loader. Loader is used for the conversion of module source code, while plug-ins can access every link of compilation through the hooks provided by Webpack to realize the extension of Webpack functions.

Implementation of the WebPack plug-in

Plugins must be an object that has an apply method. The compiler object is passed in as the first parameter to the apply method. Compiler. hooks provide hooks for every link in webPack compilation.

Here is a simple implementation of the official plug-in:

const pluginName = 'ConsoleLogOnBuildWebpackPlugin';

class ConsoleLogOnBuildWebpackPlugin {
  apply(compiler) {
    compiler.hooks.run.tap(pluginName, (compilation) = > {
      console.log('The WebPack build process begins! '); }); }}module.exports = ConsoleLogOnBuildWebpackPlugin;
Copy the code

In my tests, the following works, but sometimes we want to pass some configuration to the plug-in, so we need to use the class approach.

module.exports = {
    apply(compiler) {
        compiler.hooks.run.tap(pluginName, (compilation) = > {
            console.log('The WebPack build process begins! '); }); }};Copy the code

Or a function, which WebPack calls with the call method, and you can see that it resets the context (this) to compiler;

plugin.call(compiler, compiler);
Copy the code

Form of the Node API

Plugins can also be called using new Webpack.Progressplugin ().apply(compiler).

const webpack = require('webpack'); // Access the WebPack runtime
const configuration = require('./webpack.config.js');

let compiler = webpack(configuration);

new webpack.ProgressPlugin().apply(compiler);

compiler.run(function (err, stats) {
  // ...
});
Copy the code

Tapable

Tapable is one of the core tools of WebPack. Many objects inside WebPack are extended from the Tabable class, exposing methods such as TAP, tapAsync, and tapPromise, which plug-ins can use to access each step of the compilation to achieve custom functionality.

As shown below, Tapable exposes several synchronous and asynchronous Hook classes.

const {
    SyncHook,                      // Synchronous hooks
    SyncBailHook,                  // Synchronous fuse hook
    SyncWaterfallHook,             // Synchronous flow hook
    SyncLoopHook,                  // Synchronize loop hooks
    AsyncParallelHook,             // Asynchronous concurrent hooks
    AsyncParallelBailHook,         // Asynchronous concurrent fuse hook
    AsyncSeriesHook,               // Asynchronous serial hooks
    AsyncSeriesBailHook,           // Asynchronous serial fuse hook
    AsyncSeriesWaterfallHook       // Asynchronous serial pipeline hook
 } = require("tapable");
Copy the code

An example of SyncHook

SyncHook is a synchronous hook that calls the callbacks registered by tap in turn after calling call.

const { SyncHook } = require('tapable');
const hook = new SyncHook(['name']);
hook.tap('hello', (name) => {
    console.log(`hello ${name}`);
});
hook.tap('hello again', (name) => {
    console.log(`hello ${name}, again`);
});

hook.call('world');
// hello world
// hello world, again
Copy the code

An example of SyncBailHook

Similar to SyncHook, but if the value returned in the registered callback is not undefined, then none of the later registered callbacks will be executed.

const { SyncBailHook } = require('tapable');
const hook = new SyncBailHook(['name']);

hook.tap('hello'.(name) = > {
    console.log(`hello ${name}`);
    return false;
});

hook.tap('hello again'.(name) = > {
    console.log(`hello ${name}, again`);
});

hook.call('world');

// hello world
Copy the code

An example of SyncLoopHook

The callback function registered by SyncLoopHook will continue execution if the value returned is not undefined.

As you can see, hello world is printed three times before hello World again.

const { SyncLoopHook } = require('tapable');
const hook = new SyncLoopHook(['name']);

let index = 0;

hook.tap('hello'.(name) = > {
    console.log('hello ' + name + ' ' + index);
    return ++index === 3 ? undefined : true;
});

hook.tap('hello again'.(name) = > {
    console.log('hello ' + name + ' again');
});

hook.call('world');

// hello world 0
// hello world 1
// hello world 2
// hello world again
Copy the code

An example of SyncWaterfallHook

In the registered callback, if there is a return value, it will be used as an argument to the next registered function.

const { SyncWaterfallHook } = require('tapable');
const hook = new SyncWaterfallHook(['name']);

hook.tap('hello'.(name) = > {
    console.log('hello ' + name);
    return 'again';
});

hook.tap('hello again'.(name) = > {
    console.log('hello ' + name);
});

hook.call('world');

// hello world
// hello again
Copy the code

An example of AsyncParallelHook

Synchronous hooks can only register callbacks through TAP, while asynchronous hooks can register callbacks through TAP, tapAsync, and tapPromise.

Asynchronous hooks must be called using either callAsync or promise.

const { AsyncParallelHook } = require('tapable');

const hook = new AsyncParallelHook(['name']);

console.time('cost');

hook.tap('greeting'.function(name) {
    console.log(`hello ${name}`);
});

hook.tapAsync('greeting again'.function(name, cb) {
    setTimeout(function() {
        console.log(`hello ${name} again`);
        cb();
    }, 1000);
});

hook.tapPromise('greeting again again'.function(name) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log(`hello ${name} again again`);
            resolve();
        }, 1000);
    });
});

// hook.call('world'); // TypeError: hook.call is not a function

// hook.callAsync('world', function() {
// console.timeEnd('cost');
// });
// hello world
/ / hello world again - | ___ at the same time
// hello world again again ----|
/ / cost: 1.014 s

hook.promise('world'.function() {
console.log('done');
console.timeEnd('cost');
});
// hello world
/ / hello world again - | ___ at the same time
// hello world again again ----|
Copy the code

An example of AsyncParallelBailHook

After my tests, I found that only tap registered synchronous hook functions return non-undefined, and the following hook functions will not be executed. Other asynchronous registered hook functions do not have any effect. At first, I thought that all the functions would be effective, but later I thought it was right, asynchronous hook functions can not block.

const { AsyncParallelBailHook } = require('tapable');

const hook = new AsyncParallelBailHook(['name']);

console.time('cost');

hook.tap('greeting'.function(name) {
    console.log(`hello ${name}`);
    return false;
});

hook.tapAsync('greeting again'.function(name, cb) {
    setTimeout(function() {
        console.log(`hello ${name} again`);
        cb(false);
    }, 1000);
});

hook.tapPromise('greeting again again'.function(name) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log(`hello ${name} again again`);
            resolve();
        }, 1000);
    });
});

hook.callAsync('world'.function() {
    console.timeEnd('cost');
});

// hello world
/ / cost: 13.593 ms
Copy the code

An example of AsyncSeriesHook

const{AsyncSeriesHook example} =require('tapable');

const hook = newExamples of AsyncSeriesHook (['name']);

console.time('cost');

hook.tap('greeting'.function(name) {
    console.log(`hello ${name}`);
});

hook.tapAsync('greeting again'.function(name, cb) {
    setTimeout(function() {
        console.log(`hello ${name} again`);
        cb();
    }, 1000);
});

hook.tapPromise('greeting again again'.function(name) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log(`hello ${name} again again`);
            resolve();
        }, 1000);
    });
});

hook.callAsync('world'.function() {
    console.timeEnd('cost');
});
// hello world
// hello world again
// hello world again again
/ / cost: 2.016 s
Copy the code

An example of AsyncSeriesBailHook

Unlike asynchronous parallelism, callbacks registered after returning non-undefined in tapAsync and tapPromise are not called, but note that non-undefined must be returned before the CB call.

const { AsyncSeriesBailHook } = require('tapable');

const hook = new AsyncSeriesBailHook(['name']);

console.time('cost');

hook.tap('greeting'.function(name) {
    console.log(`hello ${name}`);
});

hook.tapAsync('greeting again'.function(name, cb) {
    setTimeout(function() {
        console.log(`hello ${name} again`);
        return false;
    }, 1000);
});

hook.tapPromise('greeting again again'.function(name) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log(`hello ${name} again again`);
            resolve();
        }, 1000);
    });
});

hook.callAsync('world'.function() {
    console.timeEnd('cost');
});

// hello world
// hello world again
Copy the code

An example of AsyncSeriesWaterfallHook

The value returned by the tap registered callback is given to the next registered callback, the second parameter of CB in the tapAsync registered callback is given to the next registered callback, and the value passed in resolve in the tapPromise registered callback is given to the next registered callback.

const { AsyncSeriesWaterfallHook } = require('tapable');
const hook = new AsyncSeriesWaterfallHook(['name']);

console.time('cost');

hook.tap('greeting'.function(name) {
    console.log(`hello ${name}`);
    return 'one';
});

hook.tapAsync('greeting again'.function(name, cb) {
    setTimeout(function() {
        console.log(`hello ${name} again`);
        cb(null.'two');
    }, 1000);
    return 'two';
});

hook.tapPromise('greeting again again'.function(name) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log(`hello ${name} again again`);
            resolve('three');
        }, 1000);
    });
});

hook.tapPromise('greeting again again again'.function(name) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log(`hello ${name} again again again`);
            resolve('three');
        }, 1000);
    });
});

hook.callAsync('world'.function() {
    console.timeEnd('cost');
});
// hello world
// hello one again
// hello two again again
// hello three again again
/ / cost: 3.016 s
Copy the code