Webpack loader 的执行机制
Webpack loader 的执行机制是 Webpack 核心功能之一。让我详细解释 loader 的执行顺序和工作原理。
1. Loader 的基本概念
Loader 就像是 Webpack 的"翻译官",负责将各种类型的文件转换为 Webpack 能够处理的 JavaScript 模块。
// webpack.config.js
module.exports = {module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']}]}
};
2. Loader 的执行顺序
2.1 从右到左,从下到上
// 示例 1:数组形式
{test: /\.css$/,use: ['style-loader', // 第三个执行'css-loader', // 第二个执行 'sass-loader' // 第一个执行]
}// 示例 2:函数形式
{test: /\.js$/,use: [{loader: 'babel-loader',options: { presets: ['@babel/preset-env'] }},'eslint-loader' // 先执行 eslint-loader,再执行 babel-loader]
}
2.2 执行流程示例
对于 .scss 文件:
{test: /\.scss$/,use: ['style-loader', // 3. 将 CSS 插入到 DOM'css-loader', // 2. 将 CSS 转换为 CommonJS'sass-loader' // 1. 将 SCSS 编译为 CSS]
}
执行顺序:sass-loader → css-loader → style-loader
3. Loader 的链式调用
每个 loader 都将前一个 loader 的结果作为输入,形成处理管道:
// 伪代码表示 loader 执行流程
const source = '/* scss source code */';// 1. sass-loader 处理
const cssCode = sassLoader(source);// 2. css-loader 处理
const jsModule = cssLoader(cssCode);// 3. style-loader 处理
const finalResult = styleLoader(jsModule);
4. Loader 的 pitch 阶段
Loader 实际上有两个执行阶段:
4.1 Normal 阶段(正常阶段)
从右到左执行 loader 函数
4.2 Pitch 阶段(前置阶段)
从左到右执行 loader 的 pitch 方法
// loader 结构
function loader(source) {// normal loader:处理文件内容return transformedSource;
}loader.pitch = function(remainingRequest, previousRequest, data) {// pitch loader:在 normal loader 之前执行// 可以中断 loader 链
};
4.3 Pitch 阶段执行顺序
// webpack 配置
use: ['loader-a', 'loader-b', 'loader-c']// 执行顺序:
// pitch-a → pitch-b → pitch-c → 读取资源 → normal-c → normal-b → normal-a
5. Loader 的类型
5.1 前置 loader (pre)
{enforce: 'pre',test: /\.js$/,loader: 'eslint-loader'
}
5.2 后置 loader (post)
{enforce: 'post', test: /\.js$/,loader: 'babel-loader'
}
5.3 普通 loader (normal)
{test: /\.css$/,loader: 'css-loader' // 默认就是 normal
}
6. 完整的执行顺序
module.exports = {module: {rules: [{enforce: 'pre',test: /\.js$/,loader: 'eslint-loader' // 1. 最先执行},{test: /\.js$/,use: ['babel-loader'] // 3. 然后执行},{enforce: 'post', test: /\.js$/,loader: 'uglify-loader' // 4. 最后执行},{test: /\.css$/,use: ['style-loader', // 6. 执行'css-loader', // 5. 执行'sass-loader' // 2. 执行(与 js loader 并行)]}]}
};
7. Loader 的开发示例
了解 loader 执行顺序有助于编写自定义 loader:
// simple-loader.js
module.exports = function(source) {console.log('Normal loader executing');return source + '\n// Processed by simple-loader';
};module.exports.pitch = function(remainingRequest, previousRequest, data) {console.log('Pitch loader executing');// 如果返回内容,会跳过后续 loader// return 'module.exports = "skipped"';
};
总结
- 执行方向:normal loader 从右到左,pitch loader 从左到右
- 优先级:pre → normal → post
- 链式处理:每个 loader 处理前一个 loader 的输出
- 可中断:pitch 阶段可以中断 loader 链
- 单一职责:每个 loader 只完成一个特定任务
理解 loader 的执行顺序对于优化构建流程和调试构建问题非常重要。
