loader的概念
webpack 需要设计出 loader 这一扩展方式?
本质上是因为计算机世界中的文件资源格式实在太多,无法穷举。那何不将”解析”资源这部分任务开放出去,由第三方实现呢?
loader 正是为了将文件资源的“读”与“处理”逻辑解耦,webpack 内部只需实现对标准 js 代码解析/处理能力,由第三方开发者以 loader 的方式补充对特定资源的解析逻辑。
webpack5 之后增加了 Parser 对象,事实上已经内置支持图片、JSON 等格式的内容,不过这并不影响我们理解loader。
loader 通常是一种 mapping 函数形式:
module.exports = function(source) {
// 执行各种代码计算
return modifySource;
};
在 webpack 进入构建阶段后,首先会通过 IO 接口读取文件内容,之后调用 LoaderRunner 并将文件内容以 source 参数形式传递到 Loader 数组,source 数据在 Loader 数组内可能会经过若干次形态转换,最终以标准 js 代码提交给 webpack 主流程,以此实现内容翻译功能。
loader函数签名:
module.exports = function(source, sourceMap?, data?) {
return source;
};
- source:资源输入,对于第一个执行的 loader 为资源文件的内容;后续执行的 loader 则为前一个 Loader 的执行结果,可能是字符串,也可能是代码的 AST 结构;
- sourceMap: 可选参数,代码的 sourcemap 结构;
- data: 可选参数,其它需要在 Loader 链中传递的信息,比如 posthtml/posthtml-loader 就会通过这个参数传递额外的 AST 对象。
其中 source 是最重要的参数,大多数 Loader 要做的事情就是将 source 转译为另一种形式的 output。
loader的链式调用模型
想要正确的处理一类资源往往不只需要一种loader,比如less:
module.exports = {
module: {
rules: [
{
test: /\.less$/i,
use: ["style-loader", "css-loader", "less-loader"],
},
],
},
}
可以看到配置了这三种loader:”style-loader”, “css-loader”, “less-loader”。
过去我们知道,这些loader作用的顺序是按照配置,从右到左调用,先调用的输出结果作为后调用的输入,所以上面的代码可以解释为:
- 首先调用 less-loader 将 less 代码转译为 css 代码;
- 将 less-loader 结果传入 css-loader,进一步将 css 内容包装成类似 module.exports = “${css}” 的 js 代码片段;
- 将 css-loader 结果传入 style-loader,在运行时调用 injectStyle 等函数,将内容注入到页面的