文章目录
前言:
Webpack是一个现代JavaScript应用的
静态模块打包器
,它能够将项目中的所有依赖项(包括JavaScript、图片、CSS等)打包成一个或多个bundle
。
在Webpack的构建过程中,存在许多生命周期钩子(hooks),这些钩子允许插件(plugins)和loaders在不同阶段介入构建过程。
Loader
- 一个
模块转换器
,它使得 Webpack 能够处理非 JavaScript 文件。Loader 在import
模块时被调用,并且可以链式调用,形成一个处理流程。 - Loader 通常用于转换文件,比如将 Sass 文件转换为 CSS,或者将图像文件转换为 DataURL。
- Loader
本身是一个函数
,它接收源文件作为输入,输出转换后的文件内容。
自定义 Loader 的实现:
我们现在的需求是,将 Markdown 文件转换为 HTML, 实现步骤
- 创建一个 JavaScript 文件作为 Loader,比如
my-loader.js
。 - 在这个文件中,导出一个函数,这个函数将对文件资源进行处理。
- 在 Webpack 配置文件中
webpack.config.js
,添加一个规则,指定该 Loader 应用到特定的文件类型上。
// my-loader.js
const marked = require("marked");
module.exports = function (source) {
// 使用marked将markdown转换为html
const html = marked(source);
return html;
};
然后在webpack.config.js
中配置这个 Loader:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.md$/,
use: "./my-loader",
},
],
},
};
Webpack生命周期:
在写plugin之前,我们先回顾下 webpack的 生命周期和内部使用的TapTable的库(用于处理事件注册和触发的)
Webpack的生命周期主要分为两个部分:Compiler
生命周期和Compilation
生命周期。
-
Compiler 生命周期是关于
整个构建过程
的,例如:beforeRun
:在开始编译之前调用。run
:在编译开始时调用。emit
:在所有资源被emit到发输目录之前调用。done
:在编译完成之后调用。
-
Compilation 生命周期是关于
单独一次编译的
,例如:buildModule
:在模块开始构建时调用。normalModuleFactory
:在模块工厂创建时调用。seal
:在编译完成,优化阶段开始之前调用。
Tapable
Tapable 是一个基于
发布订阅模式实现的事件系统
,Webpack 中的插件(Plugins)就是使用 Tapable 来监听和改变构建流程, Tapable允许开发者创建自定义的钩子,并且可以在这些钩子上注册监听函数,这些函数将在钩子被触发时执行。
Tapable 提供了多种类型的钩子,分为
同步
和异步
两大类,每种类型又有不同的行为。
安装 Tapable
首先,你需要安装 Tapable:
npm install --save tapable
钩子的类型
- SyncHook: 同步执行,无返回值。
- SyncBailHook: 同步执行,返回非
undefined
则中断。 - SyncWaterfallHook: 同步执行,返回值会传递给下一个回调。
- SyncLoopHook: 同步执行,返回非
undefined
则重新执行当前回调。 - AsyncParallelHook: 异步并行执行。
- AsyncParallelBailHook: 异步并行执行,有返回值则中断。
- AsyncSeriesHook: 异步串行执行。
- AsyncSeriesBailHook: 异步串行执行,有返回值则中断。
- AsyncSeriesWaterfallHook: 异步串行执行,返回值会传递给下一个回调。
使用 Tapable
同步钩子(SyncHook)
Sync 类型 “钩子” 执行的插件都是顺序执行的,并且只能使用
tap
注册,可以使用call
调用。
const {
SyncHook } = require('tapable');
const hook = new SyncHook(['arg1', 'arg2']);
hook.tap('myPlugin', (arg1, arg2) => {
console.log(`myPlugin: ${
arg1}, ${
arg2}`);
});
hook.call('hello', 'world'); // 输出:myPlugin: hello, world
异步钩子
异步钩子可以注册异步的回调函数,分为并行(AsyncParallelHook)
和串行(AsyncSeriesHook)
两种。
AsyncParallelHook
并行执行所有注册的异步回调,所有回调完成后执行最终的回调。
const {
AsyncParallelHook } = require('tapable');
const hook = new AsyncParallelHook(['arg1']);
hook.tapAsync('myAsyncPlugin', (arg1, callback) => {
setTimeout(() => {
console.log(`myAsyncPlugin: ${
arg1}`);
callback();
}, 1000);
});
hook.callAsync('test', () => {
console.log('所有任务执行完毕.');
});
AsyncSeriesHook
串行执行所有注册的异步回调,每个回调完成后,才会执行下一个。
const {
AsyncSeriesHook } = require('tapable');
const hook = new AsyncSeriesHook(['arg1']);
hook.tapAsync('mySeriesPlugin', (arg1, callback) => {
setTimeout(() => {
console.log(`mySeriesPlugin: ${
arg1}`);
callback();
}, 1000);
});
hook.callAsync('test', () => {
console.log('所有任务执行完毕.');
});
钩子的拦截器(Interceptor)
Tapable 还允许你为钩子添加拦截器,可以在钩子执行前后添加自定义逻辑。
const hook = new SyncHook(['arg1', 'arg2']);
hook.intercept({
call: (arg1, arg2) => {
console.log(`Before call: ${
arg1}, ${
arg2}`);
},
tap: (tapInfo) => {
console.log(`Tap registered: ${
tapInfo.name}`);
}
});
自定义 Plugin 的实现:
- 创建一个 JavaScript 文件作为 Plugin,比如
my-plugin.js
。 - 在这个文件中,定义一个类,这个类必须实现
apply
方法。 - 在
apply
方法中,接受compiler
参数,我们可以在上面注册我们的事件,并执行自定义逻辑。
// my-plugin.js
class MyPlugin {
apply(compiler) {
compiler.hooks.done.tap("MyPlugin", (stats) => {
console.log("构建完成!");
// 这里可以执行一些构建完成后的逻辑
// 将打包后的代码上传至 minio, 方便下载
});
}
}
module.exports = MyPlugin;
然后在webpack.config.js
中配置这个 Plugin:
// webpack.config.js
const MyPlugin = require("./my-plugin");
module.exports = {
plugins: [new MyPlugin()],
};