参考文章
一、如何使用 webpack 内置钩子自定义添加复杂的多入口?
为什么要利用内置钩子去实现动态的入口呢?我的理解是比如在将小程序项目以webpack工程化时,如果仅仅是在webpack.config.js中去配置 entry 的话,那么在新增页面之后,不能够watch到变化,需要重启编译。
明确思路就是手动找到所有的入口文件路径数组,调用webpack内置插件EntryPlugin插件去添加即可
// 首先挂载获取入口路径的事件方法到处理入口的钩子中,
// 仿造EnteyOptionPlugin:
import { EntryPlugin } from 'webpack'
apply (compiler) {
const { context, entry } = compiler.options;
compiler.hooks.entryOption.tap("MyEntryPlugin", (context, entry) => {
// 自己获取的多入口地址数组
const entryPath = ['pages\home\index.ts', ...] // 伪代码
entryPath.forEach(pathItem => {
const replaceExtpathItem = someReplaceFun('pages\home\index.ts') // pages\home\index
new EntryPlugin(context, pathItem, replaceExtpathItem).apply(compiler);
})
return true;
});
}
梳理 webpack 处理入口流程如下:
// webpack.js
new WebpackOptionsApply().process(options, compiler)
// WebpackOptionsApply.js
new EntryOptionPlugin().apply(compiler);
compiler.hooks.entryOption.call(options.context, options.entry);
// EntryOptionPlugin.js
const EntryPlugin = require("./EntryPlugin")
...
new EntryPlugin(context, entry, options).apply(compiler);
// EntryPlugin.js
// 在完成编译之前执行 Compiler创建了compilation之后触发了此钩子
compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
compilation.addEntry(context, dep, options, err => {
callback(err);
});
});
// Compilation.js
addEntry(context, entry, optionsOrName, callback) {
// TODO webpack 6 remove
const options =
typeof optionsOrName === "object"
? optionsOrName
: { name: optionsOrName };
this._addEntryItem(context, entry, "dependencies", options, callback);
}
...
_addEntryItem(context, entry, target, options, callback) {
// ...
this.hooks.addEntry.call(entry, options);
// ...
}
因此可以在以下两处任选一处添加处理钩子事件
- compiler.hooks.entryOption
- compiler.hooks.make.tapAsync
二、webpack 以自定的入口为主?
1.用户的配置中的plugin会先于webpack内置plugin执行
// webpack.js
if (Array.isArray(options.plugins)) {
// 运行用户配置插件
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
// 应用webpack默认配置
applyWebpackOptionsDefaults(options);
...
// 根据用户配置的webpack选项,调用内置插件
// 从上面流程分析中可知此方法调用了WebpackOptionsApply.js 的 new EntryOptionPlugin().apply(compiler)来走webpack默认的入口流程
new WebpackOptionsApply().process(options, compiler);
2.SyncBailHook 是一个同步的、保险类型的 Hook,意思是只要其中一个有返回了,后面的就不执行了
// Compiler.js
this.hooks = Object.freeze({
...
entryOption: new SyncBailHook(["context", "entry"])
...
})
因此只需要手动挂载到entryOption钩子上的事件最后以return结尾就可以了
entryOption.tap('UserCustomPluginName', () => {
// 获取入口路径
...
return true
}
基于以上两个条件我们在自定义入口处理方法中最后return就能以自定义的入口为最终确定的入口