Skip to main content

Loader 执行

前置知识

什么是pitch

Webpack 允许在 loader 函数上挂载一个名为 pitch 的函数,运行时 pitch 会比 Loader 本身更早执行。它可以阻断 loader 链。

function pitch(
// 当前 loader 之后的资源请求字符串
// 以 ! 分割组成的字符串
remainingRequest: string,
// 在执行当前 loader 之前经历过的 loader 列表
// 已经迭代过(pitch)的 loader 以 ! 分割组成的字符串
previousRequest: string,
// 与 Loader 函数的 data 相同,用于传递需要在 Loader 传播的信息
// 可以在执行 loaderA 时或者 loaderA.pitch 传递的参数
data = {}
): void {
// balabala ...
}

举个🌰

module.exports = {
module: {
rules: [
{
test: /\.less$/i,
use: [
"style-loader", "css-loader", "less-loader"
],
},
],
},
};

当执行到 css-loader.pitch 时,

// css-loader 之后的 loader 列表及资源路径
remainingRequest = less-loader!./xxx.less
// css-loader 之前的 loader 列表
previousRequest = style-loader
// 默认值
data = {}

Loader 链式执行

Loader 的执行顺序遵循后进先出(Last In First Out)。

module.exports = {
// ...
module: {
// ...
rules: [
{
test: /\.css$/,
// 执行顺序, css-loader -> style-loader
use: ['style-loader', 'css-loader'],
},
],
},
};

或者你是这样配置的 👇🏻

module.exports = {
// ...
module: {
// ...
// 执行顺序, css-loader -> style-loader
rules: [
{
test: /\.css$/,
use: {
loader: 'style-loader'
}
},
{
test: /\.css$/,
use: {
loader: 'css-loader'
}
}
]
},
// ...
}

每个loader默认的执行阶段(normal execution)的执行顺序是从 pre --> normal --> inline --> post, 即,从后往前执行。 某些情况下,loader 只关心 request 后面的元数据(metadata),并且忽略前一个 loader 的结果。 在实际执行 loader 之前,会先从左到右调用 loader 上的 pitch 方法,pitch 阶段的执行顺序是 post --> inline --> normal --> pre。对于以下 use 配置:

module.exports = {
//...
module: {
rules: [
{
//...
use: ['a-loader', 'b-loader', 'c-loader'],
},
],
},
// ...
};

pitchnormal execution 执行结果如下

|- a-loader `pitch`
|- b-loader `pitch`
|- c-loader `pitch`
|- requested module is picked up as a dependency
|- c-loader normal execution
|- b-loader normal execution
|- a-loader normal execution

正常执行

webpack-loader.png

在这个过程中如果任何 pitch 有返回值,则 loader 执行链被阻断。webpack 会跳过后面所有的的 pitchloader,直接进入上一个loadernormal execution

webpack-loader-pitch.png

更多参考 pitching-loaderRule.enforce