Skip to content

webpack中的插件,同步插件、异步插件

本节来实现webpack的插件

  1. 同步插件,打包结束后,输出一个打包成功的提醒
  2. 异步插件,打包过程中,停顿一秒再继续打包

准备一个空项目

新建一个空项目 webpack-plugin

初始化package.json

shell
yarn init -y

安装webpack

shell
yarn add webpack@^4.32.2 webpack-cli@^3.3.2 -D

新建index.js

js
// webpack\webpack-plugin\src\index.js

console.log('xzz && cheny');

新建配置文件

js
// webpack\webpack-plugin\webpack.config.js
let path = require('path')

module.exports = {
    mode: 'development',

    entry: './src/index.js',

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
}

打包测试结果 npx webpack

image-20220207201827584

分析一下源码,看看如何写插件

在使用 npx webpack 打包时

在执行编译时 会调用 compiler.js 文件,webpack\webpack-plugin\node_modules\webpack\lib\Compiler.js

image-20220207202817617

会判断配置文件中有没有配置插件,如果有,就调用插件中的apply方法,并会把这个实例传过去。

这个实例上就有很多钩子。

image-20220207203004467

实现一个同步钩子插件

打包结束后,输出一个打包成功的提醒

js
// webpack\webpack-plugin\plugins\DonePlugin.js

class DonePlugin {
    apply(compiler) {
        // done 钩子 在所有编译结束后执行
        compiler.hooks.done.tap('DonePlugin', (stats) => {
            console.log('编译完成');
        })
    }
}

module.exports = DonePlugin

修改配置文件

js
// webpack\webpack-plugin\webpack.config.js
let path = require('path')
let DonePlugin = require('./plugins/DonePlugin.js')

module.exports = {
    mode: 'development',

    entry: './src/index.js',

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },

    plugins: [
        new DonePlugin(), // 引入自己写的插件
    ]
}

打包测试结果,最后输出结果,成功

image-20220207203952472

实现一个异步钩子插件

打包过程中,停顿一秒再继续打包

js
// webpack\webpack-plugin\plugins\AsyncPlugin.js
class AsyncPlugin {
    apply(compiler) {
        compiler.hooks.emit.tapAsync('AsyncPlugin', (compliation, cb) => {
            setTimeout(() => {
                console.log('文件发射出来了,在这里等一秒');
                cb()
            }, 1000)
        })
        compiler.hooks.emit.tapPromise('AsyncPlugin', (compliation) => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log('文件发射出来了,再等一秒');
                    resolve()
                }, 1000)
            })
        })
    }
}

module.exports = AsyncPlugin

修改配置文件

js
// webpack\webpack-plugin\webpack.config.js
let path = require('path')
let DonePlugin = require('./plugins/DonePlugin.js')
let AsyncPlugin = require('./plugins/AsyncPlugin.js')

module.exports = {
    mode: 'development',

    entry: './src/index.js',

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },

    plugins: [
        new DonePlugin(), // 引入自己写的插件
        new AsyncPlugin(), // 引入自己写的插件
    ]
}

打包测试结果,间隔了一秒才继续打包,成功

image-20220207204738368

总结

webpack的插件机制,实际就是发布订阅,在new Compiler示例,构造方法执行时,就在实例上注册了几个钩子函数。

在编写插件的时候,从实例上获取到对应的钩子,注册任务,实际就是将这些任务先存储到一个数组中。

然后打包的过程中,执行到某一步时,就调用对应的任务。

webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来。

而实现这一切的核心就是Tapable。

Tapable有点类似于nodejs的events库,核心原理也是依赖发布订阅模式。

  1. 注册任务
    1. 同步注册 tap
    2. 异步注册 tapAsync、tapPromise
  2. 调用任务
    1. 同步调用 call
    2. 异步调用 callAsync、promise

在之前的章节中学习过。

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=47&spm_id_from=pageDriver

http://www.bnbiye.cn/#/articleDetail/48892d00-80dd-11ec-a014-a1cd819f9587