You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/contribute/writing-a-loader.mdx
+75-73Lines changed: 75 additions & 73 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,5 @@
1
1
---
2
-
title: Writing a Loader
2
+
title: 编写 loader
3
3
sort: 2
4
4
contributors:
5
5
- asulaiman
@@ -9,15 +9,17 @@ contributors:
9
9
- jamesgeorge007
10
10
- chenxsan
11
11
- dev-itsheng
12
+
translators:
13
+
- jsbugwang
12
14
---
13
15
14
-
A loader is a node module that exports a function. This function is called when a resource should be transformed by this loader. The given function will have access to the [Loader API](/api/loaders/) using the `this`context provided to it.
Before we dig into the different types of loaders, their usage, and examples, let's take a look at the three ways you can develop and test a loader locally.
20
+
在深入研究不同 loader 以及他们的用法和例子之前,我们先看三种本地开发测试的方法。
19
21
20
-
To test a single loader, you can use `path` to `resolve`a local file within a rule object:
To test multiple, you can utilize the `resolveLoader.modules`configuration to update where webpack will search for loaders. For example, if you had a local `/loaders`directory in your project:
By the way, if you've already created a separate repository and package for your loader, you could [`npm link`](https://docs.npmjs.com/cli/link)it to the project in which you'd like to test it out.
T> You can use [`webpack-defaults` package](https://github.com/webpack-contrib/webpack-defaults)to generate boilerplate code necessary to start writing your loader.
Synchronous loaders can`return`a single value representing the transformed module. In more complex cases, the loader can return any number of values by using the `this.callback(err, values...)`function. Errors are either passed to the `this.callback`function or thrown in a sync 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.
When multiple loaders are chained, it is important to remember that they are executed in reverse order – either right to left or bottom to top depending on array format.
In the following example, the `foo-loader`would be passed the raw resource and the `bar-loader`would receive the output of the `foo-loader`and return the final transformed module and a source map if necessary.
The following guidelines should be followed when writing a loader. They are ordered in terms of importance and some only apply in certain scenarios, read the detailed sections that follow for more information.
Loaders should do only a single task. This not only makes the job of maintaining each loader easier, but also allows them to be chained for usage in more scenarios.
119
+
loaders 应该只做单一任务。这不仅使每个 loader 易维护,也可以在更多场景链式调用。
118
120
119
-
### Chaining $#chaining$
121
+
### 链式(chaining) $#chaining$
120
122
121
-
Take advantage of the fact that loaders can be chained together. Instead of writing a single loader that tackles five tasks, write five simpler loaders that divide this effort. Isolating them not only keeps each individual loader simple, but may allow for them to be used for something you hadn't thought of originally.
Take the case of rendering a template file with data specified via loader options or query parameters. It could be written as a single loader that compiles the template from source, executes it and returns a module that exports a string containing the HTML code. However, in accordance with guidelines, an `apply-loader` exists that can be chained with other open source loaders:
125
+
以通过 loader 选项或者查询参数得到的数据渲染模板为例。可以把源代码编译为模板,执行并输出包含 HTML 代码的字符串写到一个 loader 中。但是根据用法准则,已经存在这样一个 `apply-loader`,可以将它和其他开源 loader 串联在一起调用。
124
126
125
-
-`pug-loader`: Convert template to a module that exports a function.
126
-
-`apply-loader`: Executes the function with loader options and returns raw HTML.
127
-
-`html-loader`: Accepts HTML and outputs a valid JavaScript module.
127
+
-`pug-loader`: 导出一个函数,把模板转换为模块。
128
+
-`apply-loader`: 根据 loader 选项执行函数,返回原生 HTML。
129
+
-`html-loader`: 接收 HTML,输出一个合法的 JS 模块。
128
130
129
-
T> The fact that loaders can be chained also means they don't necessarily have to output JavaScript. As long as the next loader in the chain can handle its output, the loader can return any type of module.
Keep the output modular. Loader generated modules should respect the same design principles as normal modules.
135
+
保证输出模块化。loader 生成的模块与普通模块遵循相同的设计原则。
134
136
135
-
### Stateless $#stateless$
137
+
### 无状态(stateless) $#stateless$
136
138
137
-
Make sure the loader does not retain state between module transformations. Each run should always be independent of other compiled modules as well as previous compilations of the same module.
Take advantage of the [`loader-utils`](https://github.com/webpack/loader-utils)package which provides a variety of useful tools. Along with `loader-utils`, the [`schema-utils`](https://github.com/webpack-contrib/schema-utils)package should be used for consistent JSON Schema based validation of loader options. Here's a brief example that utilizes both:
If a loader uses external resources (i.e. by reading from filesystem), they **must** indicate it. This information is used to invalidate cacheable loaders and recompile in watch mode. Here's a brief example of how to accomplish this using the `addDependency`method:
Depending on the type of module, there may be a different schema used to specify dependencies. In CSS for example, the `@import`and`url(...)`statements are used. These dependencies should be resolved by the module system.
-Using the `this.resolve`function to resolve the path.
204
+
-通过把它们转化成 `require`语句。
205
+
-使用 `this.resolve`函数解析路径。
204
206
205
-
The `css-loader`is a good example of the first approach. It transforms dependencies to `require`s, by replacing `@import`statements with a `require`to the other stylesheet and `url(...)`with a `require`to the referenced file.
In the case of the `less-loader`, it cannot transform each `@import`to a `require` because all `.less`files must be compiled in one pass for variables and mixin tracking. Therefore, the `less-loader`extends the less compiler with custom path resolving logic. It then takes advantage of the second approach, `this.resolve`, to resolve the dependency through webpack.
T> If the language only accepts relative urls (e.g. `url(file)`always refers to `./file`), you can use the `~`convention to specify references to installed modules (e.g. those in `node_modules`). In the case of `url`, that would look something like `url('~some-library/image.jpg')`.
Avoid generating common code in every module the loader processes. Instead, create a runtime file in the loader and generate a `require`to that shared module:
@@ -230,7 +232,7 @@ module.exports = function runtime(params) {
230
232
importruntimefrom'./loader-runtime.js';
231
233
232
234
exportdefaultfunctionloader(source) {
233
-
//Custom loader logic
235
+
//自定义的 loader 逻辑
234
236
235
237
return`${runtime({
236
238
source,
@@ -239,15 +241,15 @@ export default function loader(source) {
239
241
}
240
242
```
241
243
242
-
### Absolute Paths $#absolute-paths$
244
+
### 绝对路径(absolute paths) $#absolute-paths$
243
245
244
-
Don't insert absolute paths into the module code as they break hashing when the root for the project is moved. There's a [`stringifyRequest`](https://github.com/webpack/loader-utils#stringifyrequest)method in `loader-utils` which can be used to convert an absolute path to a relative one.
If the loader you're working on is a simple wrapper around another package, then you should include the package as a `peerDependency`. This approach allows the application's developer to specify the exact version in the `package.json`if desired.
For instance, the `sass-loader`[specifies`node-sass`](https://github.com/webpack-contrib/sass-loader/blob/master/package.json)as peer dependency like so:
@@ -257,9 +259,9 @@ For instance, the `sass-loader` [specifies `node-sass`](https://github.com/webpa
257
259
}
258
260
```
259
261
260
-
## Testing $#testing$
262
+
## 测试 $#testing$
261
263
262
-
So you've written a loader, followed the guidelines above, and have it set up to run locally. What's next? Let's go through a unit testing example to ensure our loader is working the way we expect. We'll be using the [Jest](https://jestjs.io/)framework to do this. We'll also install `babel-jest`and some presets that will allow us to use the `import` / `export`and`async` / `await`. Let's start by installing and saving these as a `devDependencies`:
npm install --save-dev jest babel-jest @babel/core @babel/preset-env
@@ -282,7 +284,7 @@ module.exports = {
282
284
};
283
285
```
284
286
285
-
Our loader will process `.txt`files and replace any instance of `[name]`with the `name` option given to the loader. Then it will output a valid JavaScript module containing the text as its default export:
@@ -296,15 +298,15 @@ export default function loader(source) {
296
298
}
297
299
```
298
300
299
-
We'll use this loader to process the following file:
301
+
我们将会使用这个 loader 处理以下文件:
300
302
301
303
**test/example.txt**
302
304
303
305
```bash
304
306
Hey [name]!
305
307
```
306
308
307
-
Pay close attention to this next step as we'll be using the [Node.js API](/api/node)and[`memfs`](https://github.com/streamich/memfs)to execute webpack. This lets us avoid emitting `output` to disk and will give us access to the `stats` data which we can use to grab our transformed module:
T> In this case, we've inlined our webpack configuration but you can also accept a configuration as a parameter to the exported function. This would allow you to test multiple setups using the same compiler module.
And now, finally, we can write our test and add an npm script to run it:
359
+
最后,我们来编写测试,并且添加 npm script 运行它:
358
360
359
361
**test/loader.test.js**
360
362
@@ -385,7 +387,7 @@ test('Inserts name and outputs JavaScript', async () => {
385
387
}
386
388
```
387
389
388
-
With everything in place, we can run it and see if our new loader passes the test:
390
+
准备就绪后,我们可以运行它,然后看新的 loader 是否能通过测试:
389
391
390
392
```bash
391
393
PASS test/loader.test.js
@@ -398,4 +400,4 @@ Time: 1.853s, estimated 2s
398
400
Ran all test suites.
399
401
```
400
402
401
-
It worked! At this point you should be ready to start developing, testing, and deploying your own loaders. We hope that you'll share your creations with the rest of the community!
0 commit comments