加载器是一个导出函数的 JavaScript 模块。 加载器运行器 调用此函数并将前一个加载器或资源文件的执行结果传递给它。 函数的 this
上下文由 webpack 和 加载器运行器 填充,其中包含一些有用的方法,允许加载器(除其他事项外)更改其调用方式为异步,或获取查询参数。
第一个加载器传递一个参数:资源文件的内容。 编译器期望从最后一个加载器获得结果。 结果应该是一个 String
或一个 Buffer
(转换为字符串),表示模块的 JavaScript 源代码。 还可以传递可选的 SourceMap 结果(作为 JSON 对象)。
可以在 同步模式 中返回单个结果。 对于多个结果,必须调用 this.callback()
。 在 异步模式 中,必须调用 this.async()
来指示 加载器运行器 应该等待异步结果。 它返回 this.callback()
。 然后加载器必须返回 undefined
并调用该回调函数。
/**
*
* @param {string|Buffer} content Content of the resource file
* @param {object} [map] SourceMap data consumable by https://github.com/mozilla/source-map
* @param {any} [meta] Meta data, could be anything
*/
function webpackLoader(content, map, meta) {
// code of your webpack loader
}
以下部分提供了一些不同类型加载器的基本示例。 请注意,map
和 meta
参数是可选的,请参见下面的 this.callback
。
可以使用 return
或 this.callback
以同步方式返回转换后的 content
sync-loader.js
module.exports = function (content, map, meta) {
return someSyncOperation(content);
};
this.callback
方法更加灵活,因为您可以传递多个参数,而不是仅使用 content
。
sync-loader-with-multiple-results.js
module.exports = function (content, map, meta) {
this.callback(null, someSyncOperation(content), map, meta);
return; // always return undefined when calling callback()
};
对于异步加载器,使用 this.async
来检索 callback
函数
async-loader.js
module.exports = function (content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function (err, result) {
if (err) return callback(err);
callback(null, result, map, meta);
});
};
async-loader-with-multiple-results.js
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);
});
};
默认情况下,资源文件将被转换为 UTF-8 字符串并传递给加载器。通过将 raw
标志设置为 true
,加载器将接收原始 Buffer
。每个加载器都可以将其结果作为 String
或 Buffer
传递。编译器在加载器之间进行转换。
raw-loader.js
module.exports = function (content) {
assert(content instanceof Buffer);
return someSyncOperation(content);
// return value can be a `Buffer` too
// This is also allowed if loader is not "raw"
};
module.exports.raw = true;
加载器始终从右到左调用。在某些情况下,加载器只关心请求背后的元数据,并且可以忽略先前加载器的结果。加载器上的 pitch
方法从左到右调用,在实际执行加载器(从右到左)之前调用。
对于以下 use
配置
module.exports = {
//...
module: {
rules: [
{
//...
use: ['a-loader', 'b-loader', 'c-loader'],
},
],
},
};
将执行以下步骤
|- 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
那么为什么加载器可能利用“投掷”阶段呢?
首先,传递给 pitch
方法的 data
在执行阶段也作为 this.data
公开,这对于捕获和共享循环早期信息可能很有用。
module.exports = function (content) {
return someSyncOperation(content, this.data.value);
};
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
data.value = 42;
};
其次,如果加载器在 pitch
方法中传递结果,则进程将转向并跳过剩余的加载器。在我们上面的示例中,如果 b-loader
的 pitch
方法返回了一些内容
module.exports = function (content) {
return someSyncOperation(content);
};
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
if (someCondition()) {
return (
'module.exports = require(' +
JSON.stringify('-!' + remainingRequest) +
');'
);
}
};
上面的步骤将缩短为
|- a-loader `pitch`
|- b-loader `pitch` returns a module
|- a-loader normal execution
加载器上下文表示分配给 this
属性的加载器内部可用的属性。
鉴于以下示例,此 require 调用被使用
在 /abc/file.js
中
require('./loader1?xyz!loader2!./resource?rrr');
addContextDependency(directory: string)
将目录添加为加载器结果的依赖项。
addDependency(file: string)
dependency(file: string) // shortcut
将现有文件作为加载器结果的依赖项添加,以便使它们可观察。例如,sass-loader
,less-loader
使用此方法在任何导入的 css
文件更改时重新编译。
addMissingDependency(file: string)
将一个不存在的文件作为加载器结果的依赖项添加,以便使它们可观察。类似于 addDependency
,但在编译过程中处理文件的创建,并在观察者正确附加之前。
告诉 loader-runner 加载器打算异步回调。返回 this.callback
。
一个设置可缓存标志的函数
cacheable(flag = true: boolean)
默认情况下,加载器结果被标记为可缓存的。调用此方法并传递 false
以使加载器的结果不可缓存。
可缓存的加载器在输入和依赖项没有改变时必须具有确定性的结果。这意味着加载器不应该具有除使用 this.addDependency
指定的依赖项之外的任何其他依赖项。
一个可以同步或异步调用的函数,用于返回多个结果。预期参数为
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
Error
或 null
string
或 Buffer
.如果调用此函数,则应返回 undefined 以避免加载器结果不明确。
clearDependencies();
移除加载器结果的所有依赖项,包括初始依赖项和其他加载器的依赖项。考虑使用pitch
。
模块的目录。 可用作解析其他内容的上下文。
在示例中:/abc
,因为resource.js
位于此目录中
在 pitch 阶段和正常阶段之间共享的数据对象。
emitError(error: Error)
发出一个错误,该错误也可以在输出中显示。
ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Error (from ./src/loader.js):
Here is an Error!
@ ./src/index.js 1:0-25
emitFile(name: string, content: Buffer|string, sourceMap: {...})
发出一个文件。这是 webpack 特定的。
emitWarning(warning: Error)
发出一个警告,该警告将在输出中显示,如下所示
WARNING in ./src/lib.js (./src/loader.js!./src/lib.js)
Module Warning (from ./src/loader.js):
Here is a Warning!
@ ./src/index.js 1:0-25
检查在生成的运行时代码中可以使用哪些 ES 功能。
例如:
{
// The environment supports arrow functions ('() => { ... }').
"arrowFunction": true,
// The environment supports BigInt as literal (123n).
"bigIntLiteral": false,
// The environment supports const and let for variable declarations.
"const": true,
// The environment supports destructuring ('{ a, b } = obj').
"destructuring": true,
// The environment supports an async import() function to import EcmaScript modules.
"dynamicImport": false,
// The environment supports an async import() when creating a worker, only for web targets at the moment.
"dynamicImportInWorker": false,
// The environment supports 'for of' iteration ('for (const x of array) { ... }').
"forOf": true,
// The environment supports 'globalThis'.
"globalThis": true,
// The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
"module": false,
// The environment supports optional chaining ('obj?.a' or 'obj?.()').
"optionalChaining": true,
// The environment supports template literals.
"templateLiteral": true
}
访问compilation
的inputFileSystem
属性。
提取给定的加载器选项。可选地,接受 JSON 模式作为参数。
getResolve(options: ResolveOptions): resolve
resolve(context: string, request: string, callback: function(err, result: string))
resolve(context: string, request: string): Promise<string>
创建一个类似于 this.resolve
的解析函数。
任何在 webpack resolve
选项 下的选项都是可能的。它们将与配置的 resolve
选项合并。请注意,可以在数组中使用 "..."
来扩展 resolve
选项的值,例如 { extensions: [".sass", "..."] }
。
options.dependencyType
是一个额外的选项。它允许我们指定依赖项的类型,该类型用于从 resolve
选项中解析 byDependency
。
解析操作的所有依赖项都会自动添加为当前模块的依赖项。
有关 HMR 加载器的信息。
module.exports = function (source) {
console.log(this.hot); // true if HMR is enabled via --hot flag or webpack configuration
return source;
};
this.importModule(request, options, [callback]): Promise
子编译器在构建时编译和执行请求的另一种轻量级解决方案。
request
: 从中加载模块的请求字符串options
:layer
: 指定放置/编译此模块的层publicPath
: 用于构建模块的公共路径callback
: 一个可选的 Node.js 风格回调,返回模块的导出或 ESM 的命名空间对象。如果未提供回调,importModule
将返回一个 Promise。webpack.config.js
module.exports = {
module: {
rules: [
{
test: /stylesheet\.js$/i,
use: ['./a-pitching-loader.js'],
type: 'asset/source', // we set type to 'asset/source' as the loader will return a string
},
],
},
};
a-pitching-loader.js
exports.pitch = async function (remaining) {
const result = await this.importModule(
this.resourcePath + '.webpack[javascript/auto]' + '!=!' + remaining
);
return result.default || result;
};
src/stylesheet.js
import { green, red } from './colors.js';
export default `body { background: ${red}; color: ${green}; }`;
src/colors.js
export const red = '#f00';
export const green = '#0f0';
src/index.js
import stylesheet from './stylesheet.js';
// stylesheet will be a string `body { background: #f00; color: #0f0; }` at build time
您可能会在上面的示例中注意到一些事情
!=!
语法来设置 matchResource 用于请求,即,我们将使用 this.resourcePath + '.webpack[javascript/auto]'
与 module.rules
匹配,而不是原始资源,.webpack[javascript/auto]
是 .webpack[type]
模式的伪扩展,我们使用它来指定默认的 模块类型,当没有其他模块类型指定时。它通常与 !=!
语法结合使用。请注意,上面的示例是一个简化的示例,您可以查看 webpack 存储库中的完整示例。
当前加载器在加载器数组中的索引。
在 示例 中:在 loader1 中:0
,在 loader2 中:1
loadModule(request: string, callback: function(err, source, sourceMap, module))
将给定的请求解析为模块,应用所有配置的加载器,并使用生成的源代码、sourceMap 和模块实例(通常是 NormalModule
的实例)进行回调。如果您需要知道另一个模块的源代码以生成结果,请使用此函数。
加载器上下文中的 this.loadModule
默认使用 CommonJS 解析规则。在使用不同的语义之前,请使用 this.getResolve
和适当的 dependencyType
,例如 'esm'
、'commonjs'
或自定义类型。
所有加载器的数组。它在 pitch 阶段是可写的。
loaders = [{request: string, path: string, query: string, module: function}]
在 示例 中
[
{
request: '/abc/loader1.js?xyz',
path: '/abc/loader1.js',
query: '?xyz',
module: [Function],
},
{
request: '/abc/node_modules/loader2/index.js',
path: '/abc/node_modules/loader2/index.js',
query: '',
module: [Function],
},
];
读取 webpack 运行的 mode
。
可能的值:'production'
、'development'
、'none'
options
对象,则它将指向该对象。options
,但使用查询字符串调用,则它将是一个以 ?
开头的字符串。解析后的请求字符串。
在 示例 中:'/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr'
resolve(context: string, request: string, callback: function(err, result: string))
像 require 表达式一样解析请求。
context
必须是目录的绝对路径。此目录用作解析的起始位置。request
是要解析的请求。通常使用相对请求(如 ./relative
)或模块请求(如 module/path
),但绝对路径(如 /some/path
)也可以用作请求。callback
是一个普通的 Node.js 风格的回调函数,它提供解析后的路径。解析操作的所有依赖项都会自动添加为当前模块的依赖项。
请求的资源部分,包括查询。
在 示例 中:'/abc/resource.js?rrr'
资源文件。
在 示例 中:'/abc/resource.js'
资源的查询。
在 示例 中:'?rrr'
从 webpack 4 开始,以前称为 this.options.context
的属性现在称为 this.rootContext
。
指示是否应该生成源映射。由于生成源映射可能是一项昂贵的任务,因此您应该检查是否实际需要源映射。
编译的目标。从配置选项传递。
示例值:'web'
、'node'
访问 contextify
和 absolutify
工具。
contextify
:返回一个新的请求字符串,尽可能避免使用绝对路径。absolutify
:返回一个新的请求字符串,尽可能使用绝对路径。my-sync-loader.js
module.exports = function (content) {
this.utils.contextify(
this.context,
this.utils.absolutify(this.context, './index.js')
);
this.utils.absolutify(this.context, this.resourcePath);
// …
return content;
};
加载器 API 版本。当前为 2
。这对于提供向后兼容性很有用。使用版本,您可以为重大更改指定自定义逻辑或回退。
当此加载器由 webpack 编译时,此布尔值将设置为 true。
加载器接口提供所有与模块相关的信息。但是,在极少数情况下,您可能需要访问编译器 API 本身。
因此,您应该只在万不得已的情况下使用它们。使用它们会降低加载器的可移植性。
访问 webpack 的当前 Compilation 对象。
访问 webpack 的当前 Compiler 对象。
一个布尔标志。在调试模式下设置。
从最后一个加载器传递。如果您要将输入参数作为模块执行,请考虑读取此变量以获得快捷方式(为了性能)。
指示结果是否应最小化。
将值传递给下一个加载器。如果您知道结果作为模块执行时导出的内容,请在此处设置此值(作为仅包含一个元素的数组)。
对正在加载的 Module 对象进行黑客访问。
您可以通过以下方式从加载器内部报告错误
throw
(或其他未捕获的异常)。在加载器运行时抛出错误会导致当前模块编译失败。callback
(在异步模式下)。将错误传递给回调也会导致模块编译失败。例如
./src/index.js
require('./loader!./lib');
从加载器抛出错误
./src/loader.js
module.exports = function (source) {
throw new Error('This is a Fatal Error!');
};
或者在异步模式下将错误传递给回调函数
./src/loader.js
module.exports = function (source) {
const callback = this.async();
//...
callback(new Error('This is a Fatal Error!'), source);
};
模块将像这样捆绑
/***/ "./src/loader.js!./src/lib.js":
/*!************************************!*\
!*** ./src/loader.js!./src/lib.js ***!
\************************************/
/*! no static exports found */
/***/ (function(module, exports) {
throw new Error("Module build failed (from ./src/loader.js):\nError: This is a Fatal Error!\n at Object.module.exports (/workspace/src/loader.js:3:9)");
/***/ })
然后构建输出也会显示错误(类似于this.emitError
)
ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module build failed (from ./src/loader.js):
Error: This is a Fatal Error!
at Object.module.exports (/workspace/src/loader.js:2:9)
@ ./src/index.js 1:0-25
如下所示,不仅显示错误消息,还显示有关哪些加载器和模块参与的信息
ERROR in ./src/lib.js
(./src/loader.js!./src/lib.js)
(from ./src/loader.js)
@ ./src/index.js 1:0-25
webpack v4 中引入了一种新的内联请求语法。在请求前添加<match-resource>!=!
将为该请求设置matchResource
。
设置matchResource
后,它将用于与module.rules
匹配,而不是原始资源。如果需要对资源应用更多加载器,或者需要更改模块类型,这将非常有用。它也会显示在统计信息中,并用于匹配Rule.issuer
和splitChunks
中的test
。
示例
file.js
/* STYLE: body { background: red; } */
console.log('yep');
加载器可以将文件转换为以下文件,并使用matchResource
应用用户指定的 CSS 处理规则
file.js(由加载器转换)
import './file.js.css!=!extract-style-loader/getStyles!./file.js';
console.log('yep');
这将添加对extract-style-loader/getStyles!./file.js
的依赖关系,并将结果视为file.js.css
。因为module.rules
有一个匹配/\.css$/
的规则,它将应用于此依赖关系。
加载器可能如下所示
extract-style-loader/index.js
const getStylesLoader = require.resolve('./getStyles');
module.exports = function (source) {
if (STYLES_REGEXP.test(source)) {
source = source.replace(STYLES_REGEXP, '');
return `import ${JSON.stringify(
this.utils.contextify(
this.context || this.rootContext,
`${this.resource}.css!=!${getStylesLoader}!${this.remainingRequest}`
)
)};${source}`;
}
return source;
};
extract-style-loader/getStyles.js
module.exports = function (source) {
const match = source.match(STYLES_REGEXP);
return match[0];
};
日志记录 API 自 webpack 4.37 版本发布以来可用。当在 stats 配置
中启用 logging
以及/或者当启用 基础设施日志记录
时,加载器可能会记录消息,这些消息将以相应的日志记录器格式(统计信息、基础设施)打印出来。
this.getLogger()
进行日志记录,它是 compilation.getLogger()
的快捷方式,带有加载器路径和处理后的文件。这种类型的日志记录将存储到统计信息中并相应地格式化。它可以被 webpack 用户过滤和导出。this.getLogger('name')
获取具有子名称的独立日志记录器。加载器路径和处理后的文件仍然会添加。this.getLogger ? this.getLogger() : console
,以便在使用不支持 getLogger
方法的旧版 webpack 版本时提供回退。