网上有很多webpack4、webpack5插件原理的文章,所以这块就不详细写了,这篇文章首先通过我在实际开发中举例说明,两者之间的差异,让大家有个更直观的感受,直接说插件原理个人感觉大家会理解的很抽象:
最近使用 vue create helloworld创建了项目,在引入我早期写的修改打包html内容webpack插件的时候,发现报错,看了一下原因,发现是我之前写的是使用webapck4的插件,现在项目使用的webpack5;这里就一往直前,准备把原来的插件按照webapck5重写一遍,拿出来给大家看看两者区别:
// 依赖webpack4 和 html-webpack-plugin插件
class WebpackHtmlHelpPlugin {
constructor(options) {
this.paths = options
}
apply(compiler) {
const paths = this.paths
compiler.plugin('compilation', (compilation) => {
compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(
this.constructor.name,
(htmlPluginData) => {
let str = ''
for (let i = paths.length -1; i >= 0; i--) {
paths[i].enable && (str += paths[i].content)
}
htmlPluginData.html = htmlPluginData.html.replace(
`<!-- html plugin add dom injected -->`,
str
)
}
)
})
}
module.exports = WebpackHtmlHelpPlugin
// 依赖webpack5 和 html-webpack-plugin插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
class WebpackHtmlHelpPlugin {
constructor(options) {
this.paths = options
}
apply(compiler) {
const paths = this.paths
compiler.hooks.compilation.tap('compilation', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation)
.afterTemplateExecution.tap(
this.constructor.name,
(htmlPluginData) => {
let str = ''
for (let i = paths.length -1; i >= 0; i--) {
paths[i].enable && (str += paths[i].content)
}
htmlPluginData.html = htmlPluginData.html.replace(
`<!-- html plugin add dom injected -->`,
str
)
}
)
})
}
module.exports = WebpackHtmlHelpPlugin
以上两个代码示例大家对比一下可以看出,在webpack5中使用compiler.hooks,看一下webpack5运行机制,引入网上我看到的比较好的流程图
详细运行流程图(虚线表示重复执行)
几个关键步骤
-
当我们运行webpack的时候,它会读取你的传入的配置文件(webpack.config.js), 然后运行
compile.run来初始化本质构建的参数(入口文件等),然后开始分析模块 (environment钩子函数) -
接下来进入了entryOption阶段,webpack开始读取配置的入口entry,然后开始递归的遍历所有的入口文件 (
entryOption钩子) -
webpack进入入口文件后,开始分析依赖项,进行
compilation过程。 (compilation钩子函数)- 使用配置好的loader对文件内容进行编译(
buildModule), 我们可以从传入的事件回调的参数module上拿到模块的resource(资源路径) (buildModule钩子函数)
compiler.hooks.compilation.tap('compilation', (compilation , compilationParams) => { compilation.hooks.buildModule.tap('sourceMap', (module) => { console.log(module.resource) // 资源路径 }) })- 将经过loader处理后的文件,通过acorn解析生成ast语法树(normalModuleLoader)分析文件的依赖关系,然后形成依赖数组,重复上面的模块分析过程 (webpack5 已经废弃了
normalModuleLoader这个钩子)
- 使用配置好的loader对文件内容进行编译(
-
emit阶段: 所有文件的编译及转化都已经完成,包含了最终输出的资源,我们可以在传入事件回调的compilation.assets 上拿到所需数据,其中包括即将输出的资源、代码块Chunk等等信息。