加载器是一个返回函数的node模块。
当加载器转换资源时,这个函数将被调用。
一个很简单的实例是,当只有一个加载器应用于原资源文件时,传递一个参数调用加载器:资源文件
内容是字符串.
加载器通过这个函数的this
上下文访问loader API
同步加载器可以通过return
仅返回一个值,在其他情况下,可以通过this.callback(err, values...)
返回任意数量的值,错误将传给回调函数,或者抛给一个同步加载器。
加载器被期望返回两个值,第一个值是返回JavaScript的字符串代码,第二个值是返回一个资源图的
JavaScript对象。
在复杂的情况下,当有多个加载器链式调用,只有最后一个加载器获得资源文件,仅第一个加载器会
输出两个值(JavaScript和SourceMap),其余的加载器传递值给前一个加载器。
Examples1
2
3
4// Identity loader
module.exports = function(source) {
return source;
};
1 | // Identity loader with SourceMap support |
指导方针(参考)
优先级顺序,第一个应该给最高的优先级,加载器应该:
只做一个简单的任务
加载器可以链式化,为每一步创建加载器,替代一个加载器立刻做任何事。同样的意思就是,如果
没有必要的话不必转换为JavaScript.
例如:通过应用查询参数从模板文件渲染HTML。
写一个加载器从源文件里编译模板,执行然后返回一个组件,它是一个返回了包含HTML代码的字符串
这太糟糕了。
替代方案是,为每一个任务写一个加载器然后全部调用(管道的形式)。
- jade-loader: 转换模板为一个模块输出一个函数
- apply-loader: 拿到输出函数并且返回通过应用查询参数返回行结果
- html-loader: 拿到HTML并输出一个字符串形式的模块
生产标准的模块
加载器生成的模块需要像标准的模块遵守同样的管道设计方式。
例如:这是一个糟糕的设计方式(非标准,通用场景)
1
2
3 require("any-template-language-loader!./xyz.atl");
var html = anyTemplateLanguage.render("xyz");
尽可能的标记自己为可缓存
大多数加载器可缓存,所以他们得标记自己为可缓存。
只需在加载器中调用cacheable
1
2
3
4
5 // Cacheable identity loader
module.exports = function(source) {
this.cacheable();
return source;
};不要在模块和流之间保持状态
一个加载器需要在其它加载器编译时保持独立。
一个模块先前的编译结果,和当前加载器应该保持独立.
标记依赖
如果一个加载器使用了外部资源(从文件系统读),他们必须被告知,被用来使缓存加载器无效
并且在监听方法中重新编译
1
2
3
4
5
6
7
8
9
10
11
12 // Loader adding a 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
函数去解析路径
实例一 css-loader: css-loader在样式表中替换@import
、url(...)
为require的方式去编译
依赖关系。
实例二 less-loader: less-loader不能将@import
转换为require
因为所有的less文件都会
被编译到一个临时变量中,因此,less-loader继承了less编译器的通过定义路径解析的逻辑,这个
定义逻辑使用this.resolve
通过模块系统的配置去解析文件(或者定义的模块目录等等)
如果语言之允许相对路径(比如css中:url(file)
意思就是./file
),这儿用~
的惯例去引用模块
1
2 url(file) -> require("./file")
url(~module) -> require("module")提取公共代码
不要在加载器中去处理每个模块中相同的代码,不要生成这些公共代码,在加载器中创建一个运行时
文件,通过require
来使用这些公共代码
不要嵌入绝对路劲
不要在模块代码中使用绝对路径,当项目被移动时会破坏hash,在loader-utils
中有一个stringifyRequest
的方法将绝对路径转换为相对路径。
1
2
3
4 var loaderUtils = require("loader-utils");
return "var runtime = require(" +
loaderUtils.stringifyRequest(this, "!" + require.resolve("module/runtime")) +
");";