Webpack Plugin 开发
Favori,
图:Amrit Pal Singh
Webpack 插件概念
在 Webpack 构建流程中的各个阶段、特定时机中劫持做一些代码处理、注入扩展逻辑来改变构建结果或做你想要的事情。
Webpack 插件基本架构
插件由一个构造函数实例化出来。构造函数定义 apply 方法,在安装插件时,apply 方法会被 Webpack compiler 调用一次。apply 方法可以接收一个 Webpack compiler 对象的引用
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap(
"Hello World Plugin",
(stats /* 在 hook 被触及时,会将 stats 作为参数传入。 */) => {
console.log("Hello World!");
}
);
}
}
module.exports = HelloWorldPlugin;
使用插件
// webpack.config.js
var HelloWorldPlugin = require("hello-world");
module.exports = {
// ... 这里是其他配置 ...
plugins: [new HelloWorldPlugin({ options: true })],
};
compiler 编译器
这个对象包含了 webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 webpack 实例。
hook 生命周期钩子
compiler 暴露了一些钩子
https://webpack.js.org/api/compiler-hooks/#environment
常用的钩子
beforeRun // compiler.run() 之前处理逻辑
run //在开始读取记录之前
beforeCompile //创建编译参数后执行插件
compile //在创建新编译之前立即调用
make //在完成编译之前执行
afterCompile //在完成编译之后执行
entryOption //在 webpack 选项中的 entry 配置项处理过之后
...
同步钩子的种类
- SyncHook(同步钩子) - SyncHook
- Bail Hooks(保释钩子) - SyncBailHook
- Waterfall Hooks(瀑布钩子) - SyncWaterfallHook
异步钩子的种类
- Async Series Hook(异步串行钩子) - AsyncSeriesHook
- Async waterfall(异步瀑布钩子) - AsyncWaterfallHook
- Async Series Bail - AsyncSeriesBailHook
- Async Parallel - AsyncParallelHook
- Async Series Bail - AsyncSeriesBailHook
compilation 子编译器
compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。
当 webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 compilation 将被创建。
compilation 对象也提供了很多事件回调供插件做扩展。
通过 compilation 也能读取到 compiler 对象
compiler 和 compilation 区别
compiler代表了整个 webpack 从启动到关闭的生命周期,而 compilation 只代表一次单独的编译。 compilation 是 SyncHook 同步钩子
tap
对不同钩子进行 tap 处理即可,其中 tap 方法用于同步处理,异步方式则可以调用 tapAsync 方法或 tapPromise 方法。
// tapAsync
class HelloAsyncPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync(
'HelloAsyncPlugin',
(compilation, callback) => {
// Do something async...
setTimeout(function () {
console.log('Done with async work...');
callback();
}, 1000);
}
);
}
}
module.exports = HelloAsyncPlugin;
//tapPromise
class HelloAsyncPlugin {
apply(compiler) {
compiler.hooks.emit.tapPromise('HelloAsyncPlugin', (compilation) => {
// return a Promise that resolves when we are done...
return new Promise((resolve, reject) => {
setTimeout(function () {
console.log('Done with async work...');
resolve();
}, 1000);
});
});
}
}
module.exports = HelloAsyncPlugin;
我开发的输出一个编译文件列表MarkDown的插件
const pluginName = 'FileList';
class FileListPlugin {
apply(compiler) {
compiler.hooks.emit.tap(pluginName, (compilation) => {
var filelist = '生成的文件:\n\n';
for (var filename in compilation.assets) {
filelist += '- ' + filename + '\n';
}
compilation.assets['filelist.md'] = {
source: function () {
return filelist;
},
size: function () {
return filelist.length;
},
};
});
}
}
module.exports = FileListPlugin;
至此,我们就开发了一个简单的webpack插件