-
Notifications
You must be signed in to change notification settings - Fork 0
how to write a loader.cn
A loader is a node module exporting a function
.
一个 loader 是导出一个function
的 node.js 模块
This function is called when a resource should be transformed by this loader.
当资源应由该 loader 转换时这个函数被调用。
In the simple case, when only a single loader is applied to the resource, the loader is called with one parameter: the content of the resource file as string.
在简单情况下,当只有一个 loader 应用到该资源,加载程序被调用时传入一个参数:包含资源文件内容的字符串。
The loader can access the loader API on the this
context in the function.
loader 可以在函数的this
上下文访问loader API
A sync loader that only wants to give a one value can simply return
it. In every other case the loader can give back any number of values with the this.callback(err, values...)
function. Errors are passed to the this.callback
function or thrown in a sync loader.
一个只有想给一个值的同步 loader,可以简单地return
该值。在所有其他情况下,利用this.callback(err, values...)
函数,loader 能返回任意数量的值。错误被传递给this.callback
函数或在同步 loader 中抛出。
The loader is expected to give back one or two values. The first value is a resulting JavaScript code as string or buffer. The second optional value is a SourceMap as JavaScript object.
loader 需要返回一到两个值。第一个值是处理后的字符串或 buffer 类型的 JavaScript 代码。第二个可选值是,JavaScript 对象形式的 SourceMap。
In the complex case, when multiple loaders are chained, only the last loader gets the resource file and only the first loader is expected to give back one or two values (JavaScript and SourceMap). Values that any other loader give back are passed to the previous loader.
在复杂的情况下,当多个 loader 串联,只有最后一个 loader 获取到资源文件,且只有第一个 loader 需要返回一到两个值( JavaScript 和 SourceMap )。其它 loader 返回的值被传递到前一个 loader。
// Identity loader
// 恒等型 loader
module.exports = function(source) {
return source;
};
// Identity loader with SourceMap support
// 恒等型 loader ,并支持SourceMap
module.exports = function(source, map) {
this.callback(null, source, map);
};
(Ordered by priority, first one should get the highest priority)
(按优先排序,第一个应该得到最高的优先级)
Loaders should loader 应该
Loaders can be chained. Create loaders for every step, instead of a loader that does everything at once.
loader 可以被串联调用。为每一步创建 loader,而不是一个 loader 一次就做完所有事。
This also means they should not convert to JavaScript if not necessary.
这也意味着,如果没有必要,它们不应转换为 JavaScript。
Example: Render HTML from a template file by applying the query parameters
例子:通过应用查询参数从模板文件渲染 HTML
I could write a loader that compiles the template from source, execute it and return a module that exports a string containing the HTML code. This is bad.
我可以写一个从源代码编译模板的 loader,执行它并返回一个模块,该模块输出一个包含 HTML 代码的字符串。这并不好。
Instead I should write loaders for every task in this use case and apply them all (pipeline):
相反,在这个用例中我应当写一组 loader 对应每个任务,并把它们全部用起来(管道形式)
-
jade-loader: Convert template to a module that exports a function.
-
apply-loader: Takes a function exporting module and returns raw result by applying query parameters.
-
html-loader: Takes HTML and exports a string exporting module.
-
jade-loader: 将模板转换成导出一个函数的模块。
-
apply-loader: 处理一个输出模块的函数,并通过应用查询参数返回原始结果。
-
html-loader: 处理 HTML 并输出一个用来输出模块的字符串.
Loader generated modules should respect the same design principles like normal modules.
Loader 生成的模块应像正常的模块一样,遵循相同的设计原则。
Example: That's a bad design: (not modular, global state, ...)
例如:这是一个糟糕的设计:(没有模块化,全局状态,...)
require("any-template-language-loader!./xyz.atl");
var html = anyTemplateLanguage.render("xyz");
Most loaders are cacheable, so they should flag itself as cacheable.
大多数 loader 都是可缓存的,所以他们应该标志自身为可缓存的。
Just call cacheable
in the loader.
只需在 loader 里面调用 cacheable
方法
// Cacheable identity loader
// 可缓存的恒等型 loader
module.exports = function(source) {
this.cacheable();
return source;
};
A loader should be independent of other modules compiled (expect of these issued by the loader).
一个 loader 应该独立于其他编译过的模块(期望这些特性由 loader 自身提供)。
A loader should be independent of previous compilations of the same module.
一个 loader 应该独立于同一模块过往的编译。
If a loader uses external resources (i. e. by reading from filesystem), they must tell about that. This information is used to invalidate cacheable loaders and recompile in watch mode.
如果 loader 使用了外部资源(如:从文件系统读取),他们必须记述这一点。这些信息,在监视模式下,被用来使可缓存的 loader 失效,并重新编译。
// Loader adding a header
// 添加一个 header 的 loader
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);
});
};
In many languages there is some schema to specify dependencies. i. e. in css there is @import
and url(...)
. These dependencies should be resolved by the module system.
很多语言中,都有某些规范来指定依赖。例如,在 css 中,就是@import
和url(...)
。这些依赖应该有模块系统来解析。
There are two options to do this: 有两个选项来做到这一点:
-
Transform them to
require
s. -
Use the
this.resolve
function to resolve the path -
将它们转换成
require
的形式 -
使用
this.resolve
函数来解析路径
Example 1 css-loader: The css-loader transform dependencies to require
s, by replacing @import
s with a require to the other stylesheet (processed with the css-loader too) and url(...)
with a require
to the referenced file.
例1 css-loader:css-loader 将依赖转换成require
的形式。这是通过将@import
改换成require
其他样式表(同样由 css-loader 处理)和将url(...)
改换成require
引用的文件。
Example 2 less-loader: The less-loader cannot transform @import
s to require
s, because all less files need to be compiled in one pass to track variables and mixins. Therefore the less-loader extends the less compiler with a custom path resolving logic. This custom logic uses this.resolve
to resolve the file with the configuration of the module system (aliasing, custom module directories, etc.).
例2 less-loader: less-loader 不能将@import
转换成require
的形式,因为所有 less 文件需要一次过编译,以便跟踪变量及 mixin。因此,less-loader 使用一份自定义的路径解析逻辑扩展了 less 的编译器。这份自定义的逻辑使用了this.resolve
来解析拥有模块系统配置的文件(别名化,自定义模块目录,等等...)
If the language only accept relative urls (like css: url(file)
always means ./file
), there is the ~
-convention to specify references to modules:
如果该语言只接受相对 url(如 css:url(file)
总是表示./file
),还有就是〜
-对流符以指定对模块的引用:
url(file) -> require("./file")
url(~module) -> require("module")
don't generate much code that is common in every module processed by that loader. Create a (runtime) file in the loader and generate a require
to that common code.
由某个 loader 处理的每个模块中,不要生成太多相同的代码。在 loader 中创建一个(运行时)文件,并生成引用该公共代码的require
语句.
don't put absolute paths in to the module code. They break hashing when the root for the project is moved. There is a method stringifyRequest
in loader-utils which converts an absolute path to an relative one.
不要把绝对路径放入模块代码。当项目的根目录转移时,hash 计算会被破坏。有一个方法stringifyRequest
in loader-utils,它将绝对路径转换为相对的。
Example:
var loaderUtils = require("loader-utils");
return "var runtime = require(" +
loaderUtils.stringifyRequest(this, "!" + require.resolve("module/runtime")) +
");";
using a peerDependency allows the application developer to specify the exact version in package.json
if desired. The dependency should be relatively open to allow updating the library without needing to publish a new loader version.
使用 peerDependency 允许应用程序开发人员根据需要在package.json
指定确切的版本。依赖关系应当是比较开放的,以便可以更新库,而无需发布 loader 的新版本。
"peerDependencies": {
"library": "^1.3.5"
}
there are situations where your loader requires programmable objects with functions which cannot stringified as query
-string. The less-loader, for example, provides the possibility to specify LESS-plugins. In these cases, a loader is allowed to extend webpack's options
-object to retrieve that specific option. In order to avoid name collisions, however, it is important that the option is namespaced under the loader's camelCased npm-name.
在某些情况下,loader 需要带有函数的可编程对象,自然不能序列化成query
字符串。例如,less-loader,提供了指定LESS-plugins的可能性。在这些情况下,加载器被允许扩展 webpack 的options
对象来检索特定选项。但是,为了避免名称冲突,选项基于对应 loader 的驼峰式的 npm-name 来命名是非常重要的。
Example:
// webpack.config.js
module.exports = {
...
lessLoader: {
lessPlugins: [
new LessPluginCleanCSS({advanced: true})
]
}
};
The loader should also allow to specify the config-key (e.g. lessLoader
) via query
. See discussion and example implementation.
loader 也应当允许通过query
来指定配置键(如lessLoader
)。参见讨论 和 实现样例
be added to the list of loaders
被添加到list of loaders的 loader
了解更多请阅读 loaders.