阅读 259

Webpack 5 Compilation processAssets Hook 使用

Webpack 5 之前的 plugin 中,我们常用 compiler.hooks.emit 对资源进行一些处理(比如删除注释、压缩文件等),在 Webpack 5 中使用该 hook 将会得到以下警告:

(node:3881) [DEP_WEBPACK_COMPILATION_ASSETS] DeprecationWarning: Compilation.assets will be frozen in future, all modifications are deprecated. BREAKING CHANGE: No more changes should happen to Compilation.assets after sealing the Compilation.         Do changes to assets earlier, e. g. in Compilation.hooks.processAssets.         Make sure to select an appropriate stage from Compilation.PROCESS_ASSETS_STAGE_*. (Use `node --trace-deprecation ...` to show where the warning was created) 复制代码

根据提示可知,我们需要使用 Compilation 中的 processAssets hook 来对资源进行再处理,根据官方的解释,该 hook 中有以下参数:

  • name:插件名称。

  • stagehook 的执行顺序。

  • additionalAssets:添加资源时的回调。

一直对 stageadditionalAssets 参数的使用存在疑惑(官方解释也是摸棱两可),今天抽空研究了下,把相关结论记录在此,以供大家参考。

stage

Webpack 提供了 PROCESS_ASSETS_STAGE_ADDITIONALPROCESS_ASSETS_STAGE_PRE_PROCESS 等 15 种 stage(点击查看详情),该参数主要用于控制 processAssets hook 的执行顺序,比如下面的例子:

//plugins.js const webpack = require('webpack'); class OnePlugin {   apply(compiler) {     compiler.hooks.compilation.tap('OnePlugin', (compilation) => {       compilation.hooks.processAssets.tapAsync({         name: 'OnePlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,       }, (_, callback) => {         console.log('OnePlugin PROCESS_ASSETS_STAGE_DERIVED');         callback();       });       compilation.hooks.processAssets.tapAsync({         name: 'OnePlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS,       }, (_, callback) => {         console.log('OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS');         callback();       });     });   } } class TwoPlugin {   apply(compiler) {     compiler.hooks.compilation.tap('TwoPlugin', (compilation) => {       compilation.hooks.processAssets.tapAsync({         name: 'TwoPlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,       }, (_, callback) => {         console.log('TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL');         callback();       });     });   } } module.exports = {   OnePlugin,   TwoPlugin, }; //webpack.config.js const path = require('path'); const { OnePlugin, TwoPlugin } = require('./plugins'); module.exports = {   entry: './src/index.js',   mode: process.env.NODE_ENV ?? 'development',   output: {     path: path.resolve(__dirname, 'dist'),     filename: 'bundle.js'   },   plugins: [     new OnePlugin(),     new TwoPlugin(),   ] }; 复制代码

执行 npx webpack 命令:

TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS OnePlugin PROCESS_ASSETS_STAGE_DERIVED 复制代码

通过上面的输出,可知 Webpack 在触发 processAssets hook 时,会根据指定的 stage 对应的执行顺序优先级执行相应回调,而忽略注册顺序,如果两个回调指定的 stage 相同,最先注册的最先执行,比如下面的例子:

//plugins.js const webpack = require('webpack'); class OnePlugin {   apply(compiler) {     compiler.hooks.compilation.tap('OnePlugin', (compilation) => {       compilation.hooks.processAssets.tapAsync({         name: 'OnePlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,       }, (_, callback) => {         console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL');         callback();       });     });   } } class TwoPlugin {   apply(compiler) {     compiler.hooks.compilation.tap('TwoPlugin', (compilation) => {       compilation.hooks.processAssets.tapAsync({         name: 'TwoPlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,       }, (_, callback) => {         console.log('TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL');         callback();       });     });   } } module.exports = {   OnePlugin,   TwoPlugin, }; //webpack.config.js const path = require('path'); const { OnePlugin, TwoPlugin } = require('./plugins'); module.exports = {   entry: './src/index.js',   mode: process.env.NODE_ENV ?? 'development',   output: {     path: path.resolve(__dirname, 'dist'),     filename: 'bundle.js'   },   plugins: [     new OnePlugin(),     new TwoPlugin(),   ] }; 复制代码

执行 npx webpack 命令:

OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL 复制代码

通过输出可知,在指定了相同的 stage 时,最先注册的先执行,后注册的后执行。

additionalAssets

先看下面的例子:

//plugins.js const webpack = require('webpack'); class OnePlugin {   apply(compiler) {     compiler.hooks.compilation.tap('OnePlugin', (compilation) => {       compilation.hooks.processAssets.tapAsync({         name: 'OnePlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS,         additionalAssets: true,       }, (_, callback) => {         console.log('OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS');         callback();       });       compilation.hooks.processAssets.tapAsync({         name: 'OnePlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,         additionalAssets: true,       }, (_, callback) => {         console.log('OnePlugin PROCESS_ASSETS_STAGE_DERIVED');         callback();       });       compilation.hooks.processAssets.tapAsync({         name: 'OnePlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,         additionalAssets: (_, callback) => {           console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL additionalAssets callback')           callback();         },       }, (_, callback) => {         console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL');         callback();       });       compilation.hooks.processAssets.tapAsync({         name: 'OnePlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS ,         additionalAssets: true,       }, (_, callback) => {         console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONS');         callback();       });     });   } } class TwoPlugin {   apply(compiler) {     compiler.hooks.compilation.tap('TwoPlugin', (compilation) => {       compilation.hooks.processAssets.tapAsync({         name: 'TwoPlugin',         stage: webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,       }, (_, callback) => {         console.log('TwoPlugin PROCESS_ASSETS_STAGE_DERIVED');         compilation.emitAsset('hello.txt',  new webpack.sources.RawSource('Hello World'));         callback();       });     });   } } module.exports = {   OnePlugin,   TwoPlugin, }; //webpack.config.js const path = require('path'); const { OnePlugin, TwoPlugin } = require('./plugins'); module.exports = {   entry: './src/index.js',   mode: process.env.NODE_ENV ?? 'development',   output: {     path: path.resolve(__dirname, 'dist'),     filename: 'bundle.js'   },   plugins: [     new OnePlugin(),     new TwoPlugin(),   ] }; 复制代码

执行 npx webpack 命令:

OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS OnePlugin PROCESS_ASSETS_STAGE_DERIVED TwoPlugin PROCESS_ASSETS_STAGE_DERIVED OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS OnePlugin PROCESS_ASSETS_STAGE_DERIVED OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL additionalAssets callback OnePlugin PROCESS_ASSETS_STAGE_ADDITIONS 复制代码

根据前文对 stage 的介绍,该命令的输出应该为:

OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS OnePlugin PROCESS_ASSETS_STAGE_DERIVED TwoPlugin PROCESS_ASSETS_STAGE_DERIVED OnePlugin PROCESS_ASSETS_STAGE_ADDITIONS 复制代码

但根据输出可知,在 TwoPlugin PROCESS_ASSETS_STAGE_DERIVEDOnePlugin PROCESS_ASSETS_STAGE_ADDITIONS 之间多出了以下几个输出:

OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS OnePlugin PROCESS_ASSETS_STAGE_DERIVED OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL additionalAssets callback 复制代码

这是因为在 TwoPluginprocessAssets hook 中我们通过 compilation.emitAsset 新增加了一个 hello.txt 资源,并且 OnePlugin 中已执行的几个 processAssets hook 均设置了 additionalAssets 参数,因此此刻 Webpack 先回过头触发相关的回调,然后再按照 stage 规则继续执行其它的 processAssets hook

根据上面的分析可知,参数 additionalAssets 的主要作用是为了监听后续要执行的 processAssets hook 所新增的资源,以便对其进行处理。


作者:泪已无痕
链接:https://juejin.cn/post/7047003342971043876


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐