如何封装一个plugin

关于plugin

plugin是webpack的灵魂,大部分功能都依赖于plugin的使用。在plugin中提供一系列的钩子函数方便用户在webpack的整个生命周期里做一些自定义操作。

解析plugin

plugin本身是一个向外暴露的类,引用并且实例化就可以获得这个类的功能。

在这个类上都注册了apply的原型方法,apply方法会在compiler对象创建完成后调用,并向apply方法传入一个compiler方法。

compiler对象是在初始化阶段构建的,整个webpack打包期间只有一个compiler对象,后续完成打包工作的是compiler对象内部创建的compilation。

在这里插入图片描述

plugin自定义类

首先我们先了解一下plugin中常用钩子

名称生命周期执行对象调用方式
afterPlugins启动一次新的编译compiler同步
compile创建compilation对象之前compilationParams同步
compilationcompilation对象创建完成compilation同步
emit资源生成完成,输出之前compilation异步
afterEmit资源输出到目录完成compilation异步
done完成编译stats同步

webpack 中许多对象扩展自 Tapable 类。这个类暴露 tap, tapAsync 和 tapPromise 方法,可以使用这些方法,注入自定义的构建步骤,这些步骤将在整个编译过程中不同时机触发。

封装plugin

plugin1.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const fs = require('fs')
const path = require('path')

class SelfPlugin {
constructor (options) {
console.log(options)
}

apply (compiler) {

// ? 异步
compiler.hooks.emit.tapAsync('SelfPlugin', function (compilation, cb) {
console.log('我是异步编译')

// * 修改内容
compilation.chunks.forEach(chunk => {
// chunk包含多个模块,通过chunk.modulesIterable可以遍历模块列表
for(const module of chunk.modulesIterable) {
// module包含多个依赖,通过module.dependencies进行遍历
module.dependencies.forEach(dependency => {
// console.log(dependency)
});
}
});

// * 遍历生成文件表
const manifest = {};
for (const name of Object.keys(compilation.assets)) {
manifest[name] = compilation.assets[name].size();
// 将生成文件的文件名和大小写入manifest对象
}
compilation.assets['./json/manifest.json'] = {
source() {
return JSON.stringify(manifest);
},
size() {
return this.source().length;
}
};
cb();
})
}
}

module.exports = SelfPlugin;

webpack.config.js

1
2
3
4
5
6
7
8
const SelfPlugin = require("./plugins/plugin1");

plugin: [
new SelfPlugin({
name: '自定义插件',
desc: '我是自定义插件'
}),
]