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
:插件名称。stage
:hook
的执行顺序。additionalAssets
:添加资源时的回调。
一直对 stage
和 additionalAssets
参数的使用存在疑惑(官方解释也是摸棱两可),今天抽空研究了下,把相关结论记录在此,以供大家参考。
stage
Webpack
提供了 PROCESS_ASSETS_STAGE_ADDITIONAL
、PROCESS_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_DERIVED
与 OnePlugin PROCESS_ASSETS_STAGE_ADDITIONS
之间多出了以下几个输出:
OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS
OnePlugin PROCESS_ASSETS_STAGE_DERIVED
OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL additionalAssets callback
这是因为在 TwoPlugin
的 processAssets hook
中我们通过 compilation.emitAsset
新增加了一个 hello.txt
资源,并且 OnePlugin
中已执行的几个 processAssets hook
均设置了 additionalAssets
参数,因此此刻 Webpack
先回过头触发相关的回调,然后再按照 stage
规则继续执行其它的 processAssets hook
。
根据上面的分析可知,参数 additionalAssets
的主要作用是为了监听后续要执行的 processAssets hook
所新增的资源,以便对其进行处理。
转载自:https://juejin.cn/post/7047003342971043876