webpack工作流程
webpack根据打包流程定义了不同阶段的事件集。在打包过程中,通过调用预先在事件集中注册事件,实现特定的处理功能,完成自定义打包
plugin 即是通过webpack事件机制,执行具体自定义任务的抽象接口
Plugin基本结构
class Plugin {
constructor(option) {}
// 为事件添加监听函数
apply(complier) {}
}
plugin执行流程
- webpack初始化后,在读取plugins属性时初始化所有plugins插件
- 通过调用插件的apply函数(应用complier参数)将处理功能函数注册至webpack各个阶段中
- webpack按事件流的触发顺序,调用plugin在各个阶段注册的函数(应用compilation参数获取当前编译对象)
事件机制
webpack内部通过Tapable库实现事件机制(注册、触发): 详情看tapbale
Webpack主要对象
- compiler: 完整的webpack环境配置对象。
- complilation: 一次资源版本构建对象。每当检测到一个文件变化,就会创建一个新的compilation,生成一组新的编译资源; complilation对象包含了当前模块资源、编译生成资源、变化文件、跟踪依赖的状态信息; 同时以提供了很多关键时机的回调,以供插件做自定义处理
Compiler(编译实例)
class Compiler {
constructor(context) {
// 定义各阶段(生命周期)的hooks名称
this.hooks = Object.freeze({
done: new AsyncSeriesHook(["stats"]),//一次编译完成后执行,回调参数:stats
beforeRun: new AsyncSeriesHook(["compiler"]),
run: new AsyncSeriesHook(["compiler"]),//在编译器开始读取记录前执行
emit: new AsyncSeriesHook(["compilation"]),//在生成文件到output目录之前执行,回调参数: compilation
afterEmit: new AsyncSeriesHook(["compilation"]),//在生成文件到output目录之后执行
compilation: new SyncHook(["compilation", "params"]),//在一次compilation创建后执行插件
beforeCompile: new AsyncSeriesHook(["params"]),
compile: new SyncHook(["params"]),//在一个新的compilation创建之前执行
make:new AsyncParallelHook(["compilation"]),//完成一次编译之前执行
afterCompile: new AsyncSeriesHook(["compilation"]),
watchRun: new AsyncSeriesHook(["compiler"]),
failed: new SyncHook(["error"]),
watchClose: new SyncHook([]),
afterPlugins: new SyncHook(["compiler"]),
entryOption: new SyncBailHook(["context", "entry"])
});
// WebpackOptions: webpack.config.js配置信息
this.options = {}
// webpack构建函数
this.webpack = webpack
// 上下文路径 {string}
this.context = context
// ...
}
// 创建Compilation对象
newCompilation(params) {}
// watch监听
watch() {}
// 打包流程(即完整的生命周期)
run(callback) {}
// 编译流程
compile(callback) {}
// 生成打包文件
emitAssets(compilation, callback) {}
}
详细看www.webpackjs.com/api/compile…
Compilation(正在编译实例)
每次监听到新的编译(或新的构建),如chunks,就会创建一个新的 compilation,执行编译
与Compiler用法类似,属性中有compiler的指针
详细看www.webpackjs.com/api/compila…
自定义Plugin
class MyPlugin {
// 自定义配置
constructor(options) {
this.filename = options.filename
}
apply(complier) {
// context 为 运行指令的目录
const context = compiler.options.context // process.cwd()
// 在各个阶段添加执行函数(钩子)
compiler.hooks.emit.tap('pluginName', (complication) => {
// (1)在compiler钩子阶段执行
// (2)在compliation钩子阶段执行
compilation.hooks.additionalAssets.tapAsync('name', (cb) => {
const content = 'hello world'
// 方式一:
compilation.assets['a.txt'] = {
source() { return content },
size() {return content.length}
}
// 方式二:
compliation.assets['b.txt'] = new RawSource(content)
// 方法三:
compliation.emitAsset('c.txt', new RawSource(content))
cb()
})
})
}
}