huruji / blog

红日初升,其道大光:sun_with_face::house_with_garden:请star或watch,不要fork
https://juejin.im/user/5894886f2f301e00693a3e49/posts
158 stars 11 forks source link

通过 Webpack 的 compiler 对象的 Hooks 学会编写 Webpack 插件的编写 #57

Open huruji opened 5 years ago

huruji commented 5 years ago

webpack 的 Hooks

Webpack 的 Compiler 对象主要有以下 Hooks:

webpack 处理完 entry 配置项后触发,这是一个同步串行的 SyncBailHook 钩子,只要监听函数有一个函数的返回值不为undefined,则直接跳过剩下逻辑

无参数

处理完初始化插件后触发,这是一个同步的 SyncHook 钩子,不关心返回值

参数是 compiler 对象

Resolve 安装完成后触发,这是一个同步的 SyncHook 钩子

参数是 compiler 对象

environment 准备好后触发,这是一个 SyncHook 钩子

无参数

environment 安装完成后触发,这是一个 SyncHook 钩子

compiler.run 函数之前触发,这是一个异步串行 AsyncSeriesHook 钩子

参数是 compiler

开始读取 records 之前触发,这是一个异步串行 AsyncSeriesHook 钩子

参数是 compiler

监听模式下,一个新的编译开始之前触发,这是一个异步串行的 AsyncSeriesHook 钩子

参数是 compiler

normalModuleFactory 创建之后触发,这是一个同步 SyncHook 钩子

参数是 normalModuleFactory

contextModuleFactory 创建之后触发,

参数是 contextModuleFactory

编译参数创建之后触发,这是一个异步串行 AsyncSeriesHook 钩子

参数是 compilationParams

一个新的编译创建之后触发,这是一个同步 SyncHook 钩子

参数是 compilationParams

触发 compilation 之前触发,这个是一个同步 SyncHook 钩子

参数是 compilation

编译创建之后执行,这是一个同步 SyncHook 钩子

参数是 compilation

这是一个异步并发 AsyncParallelBailHook 钩子

参数是 compilation

这是一个异步串行 AsyncSeriesHook 钩子

参数是 compilation

这是一个 SyncBailHook 钩子

参数是 compilation

生成资源到 output 目录之前触发,这是一个异步串行 AsyncSeriesHook 钩子

参数是 compilation

生成资源到 output 目录之后,这是一个异步串行 AsyncSeriesHook 钩子

参数是 compilation

编译完成后触发,这是一个异步串行 AsyncSeriesHook 钩子

参数是 stats

编译失败触发,这是一个同步 SyncHook 钩子

参数是 error

监听模式下,编译无效时触发,这是一个同步 SyncHook 钩子

参数是 fileName,changeTime

+ watchClose

监听模式停止,一个同步 SyncHook 钩子

插件编写实例

webpack-clear-console

通过实例学习是最快的,让我们看一个最简单的例子,webpack-clear-console,这个插件是去除输出里的 console 调用,里面插件的写法是 webpack4 之前的写法,不过基本上是一致的,通过源码可以看到插件在 emit 这个钩子上(生成资源到output目录之前)触发,通过 compilation 对象的 assets 对象的 source 方法获取文件内容,然后进行正则匹配。 最后需要将 source 和 size 的变动归回原来的 compilation 对象中,否则这些变动是不会生效的

island-webpack-plugin

island-webpack-plugin 是一个在 bundle 中添加作者信息的插件,这个插件同样是在 emit 这个钩子上触发的,同样是获取 source 后对 source 添加作者信息的字符串。

emit 是一个异步的钩子,可以使用 promise 的 如下,可以使用 promise 对上面的插件进行简单改造,以 island-webpack-plugin 为例,改造如下:

class AuthorPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.emit.tap('author-plugin', (compilation) => {
            const options = this.options
            return new Promise((resolve, reject) => {
                const assets = compilation.assets
                Object.keys(assets).forEach(e => {
                    let source = assets[e].source()
                    let info = []

                    if (options.author) info.push(`@Author: ${options.author}`)
                    if (options.email) info.push(`@Email: ${options.email}`)
                    if (options.homepage) info.push(`@Homepage: ${options.homepage}`)

                    if (info.length) {
                        info.push(`@Date: ${new Date()}`)
                        source = `/*\n  ${info.join('\n\n  ')}\n*/\n${source}`
                    }

                    compilation.assets[e].source = () => source
                    compilation.assets[e].size = () => source.size
                })
                resolve()
            })
        })
    }
}

module.exports = AuthorPlugin