loader是导出function
的节点模块。
当资源应该由此loader转换时,调用此函数。
在简单的情况下,当只有一个loader应用于资源时,调用loader有一个参数:作为字符串的资源文件的内容。
这个loader能够在这个函数的上下文中this
中可以访问 [[loader API | loaders]]。
一个同步loader可以通过return
来返回这个值。在其他情况下,loader可以通过this.callback(err, values...)
函数返回任意数量的值。错误会被传到this.callback
函数或者在同步loader中抛出。
这个loader应该返回一个或者两个值。第一个值是JavaScript代码产生的字符串或者缓冲区。第二个可选的值是JavaScript对象的SourceMap。
在复杂的情况下,当多个loaders被链接的时候,只有最后一个loader能够获取资源文件并且只有第一个loader预期返回一个或者两个值(JavaScript和SourceMap)。其它任何loader返回的值会传到之前的loader中。
// 定义loader
module.exports = function(source) {
return source;
};
// 通过SourceMap支持定义loader
module.exports = function(source, map) {
this.callback(null, source, map);
};
(按照优先级排序,第一个具有最高的优先级)
这也意味着不必须的话它们不应该转换成JavaScript。
例子:通过应用查询参数来将模板文件渲染成HTML。
我可以写一个能够将源文件编译成模板的loader,执行并且返回一个模板,这个模板能够导出一个包含HTML代码的字符串。这样是不好的。
相反,我应该为这个用例中的每一个任务写入loaders并且应用它们(管道):
Loader生成的模块应遵循与常规模块相同的设计原则。
例子:这是一个不好的设计:(非标准化的,全局状态,...)
require("any-template-language-loader!./xyz.atl");
var html = anyTemplateLanguage.render("xyz");
大多数loaders是可以缓存的,因此它们应该把自身标志成可缓存的。
只要在load中调用cacheable
。
// 利用cacheable定义loader
module.exports = function(source) {
this.cacheable();
return source;
};
loader应该和其它编译后的模块相互独立。(除了能够被loader处理的这些问题)
loader应该和相同模块的之前汇编相互独立。
如果loader使用外部资源(比如读文件系统),它们必须说明。这个信息被用来废弃可缓存的loader并且在观察模式下重新编译。
// 在loader中添加header
var path = require("path");
module.exports = function(source) {
this.cacheable();
var callback = this.async();
var headerPath = path.resolve("header.js");
this.addDependency(headerPath);
fs.readFile(headerPath, "utf-8", function(err, header) {
if(err) return callback(err);
callback(null, header + "\n" + source);
});
};
在很多语言中存在某些机制来规定依赖,比如在css里面使用@import
以及url(...)
。这些依赖可以通过模块系统来解析。
存在两个选项:
require
s。
this.resolve
函数来解析路径。
例子1 css-loader
:css-loader
将依赖转换成 require
,通过使用引入其它样式表(也是通过css-loader
来处理)来代替@import
以及通过require
其它的引用文件来代替url(...)
。
例子2 less-loader
:less-loader
不能够将@import
转换成 require
,因为所有的less文件需要一起编译来跟踪变量和mixins。因此 less-loader
通过一个定制的路径解析逻辑来拓展less编译器。这个定制的逻辑使用this.resolve
通过模块系统的配置(别名使用,定制的模块目录,等等)来解析文件。
如果语言只支持相对路径(比如在css中:url(file)
总是表示./file
),利用~
约定来规定模块的引用。
url(file) -> require("./file")
url(~module) -> require("module")
不生成过多在每个loader中么个模块都会处理的共用代码。在loader中创建一个(运行期)文件并且创建对共用代码的require
。
不要将绝对路径放入模块代码中。当项目根路径被移动的时,它们会破坏散列函数。在loader-utils中有stringifyRequest
这个方法能够将绝对路径转成相对路径。
例子:
var loaderUtils = require("loader-utils");
return "var runtime = require(" +
loaderUtils.stringifyRequest(this, "!" + require.resolve("module/runtime")) +
");";
peerDependencies
来包装library
用开发者能够在package.json
里面规定具体的版本。依赖关系应该相对开放从而允许在不需要发布新的loader版本的时候更新library。
"peerDependencies": {
"library": "^1.3.5"
}
query
选项
在某些情况下,你的loader需要可编程对象,其函数不能作为query
字符串进行字符串化。例如,less-loader
提供了指定LESS-plugins的可能性。在这些情况下,允许loader拓展webpack的options
对象来检索特定选项。然而,为了避免名称冲突,重要的是该选项在loader的驼峰npm-name下的命名空间。
示例:
// webpack.config.js
module.exports = {
...
lessLoader: {
lessPlugins: [
new LessPluginCleanCSS({advanced: true})
]
}
};
loader还应该允许通过query
来指定config-key(比如lessLoader
)。 See 讨论 and 案例实现.