同步、异步loader
如果是单个处理结果,可以在 同步模式 中直接返回。如果有多个处理结果,则必须调用 this.callback()
。在 异步模式 中,必须调用 this.async()
来告知 loader runner 等待异步结果,它会返回 this.callback()
回调函数。随后 loader 必须返回 undefined
并且调用该回调函数。
"Raw" Loader
默认情况下,资源文件会被转化为 UTF-8 字符串,然后传给 loader。通过设置 raw
为 true
,loader 可以接收原始的 Buffer
。每一个 loader 都可以用 String
或者 Buffer
的形式传递它的处理结果。complier 将会把它们在 loader 之间相互转换。
示例
示例1: 同步使用callback
javascript
module.exports = function (content, map, meta) {
this.callback(null, someSyncOperation(content), map, meta);
return; // 当调用 callback() 函数时,总是返回 undefined
};
javascript
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
- 第一个参数必须是
Error
或者null
- 第二个参数是一个
string
或者Buffer
。 - 可选的:第三个参数必须是一个可以被 this module 解析的 source map。
- 可选的:第四个参数,会被 webpack 忽略,可以是任何东西(例如一些元数据)
提示
如果希望在 loader 之间共享公共的 AST,可以将抽象语法树 AST(例如ESTree
)作为第四个参数(meta
)传递,以加快构建时间。
示例2: 异步使用async
javascript
module.exports = function (content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function (err, result, sourceMaps, meta) {
if (err) return callback(err);
callback(null, result, sourceMaps, meta);
});
};
示例3: raw-loader
javascript
module.exports = function (content) {
assert(content instanceof Buffer);
return someSyncOperation(content);
// 返回值也可以是一个 `Buffer`
// 即使不是 "raw",loader 也没问题
};
module.exports.raw = true;
loader执行过程
所有一个接一个地进入的 loader,都有两个阶段:
- Pitching 阶段: loader 上的 pitch 方法,按照
后置(post)、行内(inline)、普通(normal)、前置(pre)
的顺序调用。更多详细信息,请查看 Pitching Loader。 - Normal 阶段: loader 上的 常规方法,按照
前置(pre)、普通(normal)、行内(inline)、后置(post)
的顺序调用。模块源码的转换, 发生在这个阶段。
对于以下 use
配置:
javascript
module.exports = {
//...
module: {
rules: [
{
//...
use: ['a-loader', 'b-loader', 'c-loader'],
},
],
},
};
将会发生这些步骤:
diff
|- 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
为什么 loader 可以利用 "pitching" 阶段呢
传递给
pitch
方法的data
,在执行阶段也会暴露在this.data
之下,并且可以用于在循环时,捕获并共享前面的信息javascriptmodule.exports = function (content) { return someSyncOperation(content, this.data.value); // 42 }; module.exports.pitch = function (remainingRequest, precedingRequest, data) { data.value = 42; };
如果某个 loader 在
pitch
方法中给出一个结果,那么这个过程会回过身来,并跳过剩下的 loaderjavascriptmodule.exports = function (content) { return someSyncOperation(content); }; module.exports.pitch = function (remainingRequest, precedingRequest, data) { if (someCondition()) { return ( 'module.exports = require(' + JSON.stringify('-!' + remainingRequest) + ');' ); } };
上面的步骤将被缩短为:
diff|- a-loader `pitch` |- b-loader `pitch` returns a module |- a-loader normal execution