07_01_定义加载器(Webpack Book)
Loader Definitions
定义加载器
Webpack provides multiple ways to set up module loaders. Webpack 2 simplified the situation by introducing the use
field. It can be a good idea to prefer absolute paths here as they allow you to move configuration without breaking assumptions.
Webpack提供多种设置模块加载器的方法。在Webpack 2 中引入use
来简化这种情况。在这里选择绝对路径可能是一个好主意,因为它们允许您在不破坏假设的情况下移动配置。
The other way is to set context
field as this gives a similar effect and affects the way entry points and loaders are resolved. It doesn't have an impact on the output, though, and you still need to use an absolute path or /
there.
另一个方法是设置context
字段,因为这会产生类似的效果,同时也影响入口点和加载器的解析方式。但是,它不会影响输出,你仍然需要在那里使用绝对路径或者/
Assuming you set an include
or exclude
rule, packages loaded from node_modules still work as the assumption is that they have been compiled in such a way that they work out of the box. If they don't, then you have to apply techniques covered in the Consuming Packages chapter.
假设您设置了一个include
或exclude
规则,从node_modules加载的包仍然可以工作,因为它们是以一种开箱即用的方式编译的。如果不能用,你就需要用到 Consuming Packages 中提到的技术了。
T> include
/exclude
is handy with node_modules as webpack processes and traverses the installed packages by default when you import JavaScript files to your project. Therefore you need to configure it to avoid that behavior. Other file types don't suffer from this issue.
当您将JavaScript文件导入到项目中时,“include”/“exclude”与*node_modules一起作为webpack处理和遍历已安装包方式时非常方便。因此你需要配置以免发生这个情况。其他文件类型没有这个问题。
Anatomy of a Loader(加载器结构)
Webpack supports a large variety of formats through loaders. Also, it supports a couple of JavaScript module formats out of the box. The idea is the same. You always set up a loader, or loaders, and connect those with your directory structure.
Webpack通过 loaders 支持各种各样的格式。它也直接支持一些Javascript模块格式。方法是一样的。你始终设置一个或多个 loader,并且将他们与你的目录结构相关联。
{pagebreak}
Consider the example below where webpack processes JavaScript through Babel:
参考以下通过 Babel 处理 Javascript的示例:
webpack.config.js
module.exports = { ... module: { rules: [ { // **Conditions** to match files using RegExp, function. test: /\.js$/, // **Restrictions** // Restrict matching to a directory. This // also accepts an array of paths or a function. // The same applies to `exclude`. include: path.join(__dirname, "app"), exclude(path) { // You can perform more complicated checks as well. return path.match(/node_modules/); }, // **Actions** to apply loaders to the matched files. use: "babel-loader", }, ], }, };
T> If you are not sure how a particular RegExp matches, consider using an online tool, such as regex101, RegExr, or Regexper.
如果你不确定一个正则表达式如何匹配字符串上,可以考虑使用在线工具,如: regex101, RegExr, or Regexper.
Loader Evaluation Order(加载器计算顺序)
It's good to keep in mind that webpack's loaders are always evaluated from right to left and from bottom to top (separate definitions). The right-to-left rule is easier to remember when you think about as functions. You can read definition use: ["style-loader", "css-loader"]
as style(css(input))
based on this rule.
最好记住Webpack的加载器总是从右到左从下到上(分离的定义)计算。从右到左的原则比较容易记住,因为和函数一样。根据这个原则,你可以把use:["style-loader", "css-loader"]
当成style(css(input))
。
To see the rule in action, consider the example below:
参考以下示例来了解这个规则的应用:
{ test: /\.css$/, use: ["style-loader", "css-loader"], },
Based on the right to left rule, the example can be split up while keeping it equivalent:
基于从右到左的规则,上面的例子可以被拆分成以下结构,而功能保持一致:
{ test: /\.css$/, use: "style-loader", }, { test: /\.css$/, use: "css-loader", },
Enforcing Order(强制顺序)
Even though it would be possible to develop an arbitrary configuration using the rule above, it can be convenient to be able to force specific rules to be applied before or after regular ones. The enforce
field can come in handy here. It can be set to either pre
or post
to push processing either before or after other loaders.
尽管使用上面的规则可以开发任意的配置,但是强制特定的规则在普通规则之前或之后执行还是非常方便的。enforce
字段可以方便的达到这个目的。把它可以被设置成pre
或post
, 可以在其他加载器之前或者之后处理执行。
Linting is a good example because the build should fail before it does anything else. Using enforce: "post"
is rarer and it would imply you want to perform a check against the built source. Performing analysis against the built source is one potential example.
Linting是一个很好的例子,因为如果Linting报错,Build过程应该在执行其他操作前就失败。enforce:"post"
更少见,这意味着你要对生成的的源码进行检查。对生成的源码进行分析算是一种应用情形。
{pagebreak}
The basic syntax goes as below:
以下是基本语法:
{ // Conditions test: /\.js$/, enforce: "pre", // "post" too // Actions use: "eslint-loader", },
It would be possible to write the same configuration without enforce
if you chained the declaration with other loaders related to the test
carefully. Using enforce
removes the necessity for that and allows you to split loader execution into separate stages that are easier to compose.
如果你仔细地将相关test
加载器的声明链接起来而不使用enforce
来得到相同效果的配置是可能的。使用enforce
可以移除 顺序 的必要性,并让你很容易地将加载器的执行放在不同阶段。
Passing Parameters to a Loader(向加载器传递参数)
There's a query format that allows passing parameters to loaders:
有一个查询格式允许传递参数给加载器。
{ // Conditions test: /\.js$/, include: PATHS.app, // Actions use: "babel-loader?presets[]=env", },
This style of configuration works in entries and source imports too as webpack picks it up. The format comes in handy in certain individual cases, but often you are better off using more readable alternatives.
这种样式的配置在 entries 和 源码导入中也适用,因为Webpack也采用了这个格式。这种格式在特定情况非常方便,但是你最好使用更易读的替代方案。
{pagebreak}
It's preferable to go through use
:
最好通过使用use
:
{ // Conditions test: /\.js$/, include: PATHS.app, // Actions use: { loader: "babel-loader", options: { presets: ["env"], }, }, },
If you wanted to use more than one loader, you could pass an array to use
and expand from there:
如果你想使用不只一个加载器,可以传递一个数组给use
并在里面展开:
{ test: /\.js$/, include: PATHS.app, use: [ { loader: "babel-loader", options: { presets: ["env"], }, }, // Add more loaders here ], },
{pagebreak}
Branching at use
Using a Function(在use
中使用函数进行分支)
In the book setup, you compose configuration on a higher level. Another option to achieve similar results would be to branch at use
as webpack's loader definitions accept functions that allow you to branch depending on the environment. Consider the example below:
在书中的设置,在一个较高层级组合配置。实现类似结果的另一个选项是在use
处进行分支,因为webpack的加载器定义接受允许您根据环境进行分支的函数。参考下面的例子:
{ test: /\.css$/, // `resource` refers to the resource path matched. // `resourceQuery` contains possible query passed to it // `issuer` tells about match context path use: ({ resource, resourceQuery, issuer }) => { // You have to return something falsy, object, or a // string (i.e., "style-loader") from here. // // Returning an array fails! Nest rules instead. if (env === "development") { return { use: { loader: "css-loader", // css-loader first rules: [ "style-loader", // style-loader after ], }, }; } }, },
Carefully applied, this technique allows different means of composition.
谨慎使用,因为这种技术允许不同的组合方式。
Inline Definitions(内联定义)
Even though configuration level loader definitions are preferable, it's possible to write loader definitions inline:
即使配置级加载器定义更好,也可以内联编写加载器定义:
// Process foo.png through url-loader and other // possible matches. import "url-loader!./foo.png"; // Override possible higher level match completely import "!!url-loader!./bar.png";
The problem with this approach is that it couples your source with webpack. Nonetheless, it's still an excellent form to know. Since configuration entries go through the same mechanism, the same forms work there as well:
这个方法的问题在于让你的代码与Webpack相耦合。尽管如此,内联定义也是一个不错的格式。由于配置入口使用相同的机制,所以该格式也适用:
{ entry: { app: "babel-loader!./app", }, },
Alternate Ways to Match Files(匹配文件的其他方式 )
test
combined with include
or exclude
to constrain the match is the most common approach to match files. These accept the data types as listed below:
“test”与“include”或“exclude”组合来约束匹配是最常见的匹配文件的方法。以下是可接受的数据类型:
test
- Match against a RegExp, string, function, an object, or an array of conditions like these. (通过正则表达式,字符串,函数,对象,条件数组等等匹配。)include
- The same.(同上)exclude
- The same, except the output is the inverse ofinclude
.(同上,除了输出与"include"相反).resource: /inline/
- Match against a resource path including the query. Examples:/path/foo.inline.js
,/path/bar.png?inline
.(匹配包含查询的资源路径。)issuer: /bar.js/
- Match against a resource requested from the match. Example:/path/foo.png
would match if it was requested from/path/bar.js
.(来自匹配项的资源请求来匹配)resourcePath: /inline/
- Match against a resource path without its query. Example:/path/foo.inline.png
.(匹配不包含查询的资源路径)resourceQuery: /inline/
- Match against a resource based on its query. Example:/path/foo.png?inline
.(匹配基于查询的资源)
Boolean based fields can be used to constrain these matchers further:
布尔值可以用来更好的约束匹配器:
not
- Do not match against a condition (seetest
for accepted values).(不要通过条件匹配)and
- Match against an array of conditions. All must match.(通过条件数据匹配。条件数组全部满足)or
- Match against an array while any must match.(通过条件数据匹配。条件数组任意项必须满足)
Loading Based on resourceQuery
(基于resourceQuery
加载)
oneOf
field makes it possible to route webpack to a specific loader based on a resource related match:oneOf
字段可以将Webpack跳转到相应资源的匹配项的加载器上:
{ test: /\.png$/, oneOf: [ { resourceQuery: /inline/, use: "url-loader", }, { resourceQuery: /external/, use: "file-loader", }, ], },
If you wanted to embed the context information to the filename, the rule could use resourcePath
over resourceQuery
.
如果你想把上下文信息嵌入到文件名中,规则应该使用resourcePath
而不是resourceQuery
。
{pagebreak}
Loading Based on issuer
(基于issuer
加载)
issuer
can be used to control behavior based on where a resource was imported. In the example below adapted from css-loader issue 287, style-loader is applied when webpack captures a CSS file from a JavaScript import:issuer
可以根据导入位置来控制行为。下面的示例是基于css-loader issue 287,当Wepack捕获到JavaScript导入一个CSS文件时,style-loader就会运行。
{ test: /\.css$/, rules: [ { issuer: /\.js$/, use: "style-loader", }, { use: "css-loader", }, ], },
Another approach would be to mix issuer
and not
:
另一个方法就是混合使用issuer
和not
:
{ test: /\.css$/, rules: [ // CSS imported from other modules is added to the DOM { issuer: { not: /\.css$/ }, use: "style-loader", }, // Apply css-loader against CSS imports to return CSS { use: "css-loader", }, ], }
Understanding Loader Behavior(理解加载器行为)
Loader behavior can be understood in greater detail by inspecting them. loader-runner allows you to run them in isolation without webpack. Webpack uses this package internally and Extending with Loaders chapter covers it in detail.
通过检查他们,可以更深入的了解加载器行为。loader-runner允许你在没有Webpack的情况下独立运行他们。Webpack在内部使用这个包,并且在Extending with Loaders一节有更详细的介绍。
inspect-loader allows you to inspect what's being passed between loaders. Instead of having to insert console.log
s within node_modules, you can attach this loader to your configuration and inspect the flow there.
inspect-loader让你检查加载器之间传递的内容。不需要在node_modules中添加consloe.log
,你能将这个加载器添加到你的配置中并可以检查流程。
Conclusion(总结)
Webpack provides multiple ways to setup loaders but sticking with use
is enough in webpack 4. Be careful with loader ordering, as it's a common source of problems.
Webpack提供多种方式设置加载器,但是在Webpack4中坚持使用use
就足够了。要小心处理加载器的顺序,因为大部分原因都是它导致的。
To recap:(收获)
- Loaders allow you determine what should happen when webpack's module resolution mechanism encounters a file.
Loaders 让你决定当Webpack处理机制碰到一个文件时该如何处理。 - A loader definition consists of conditions based on which to match and actions that should be performed when a match happens.
加载器定义由要匹配的conditions和在匹配发生时应该执行的action组成。 - Webpack 2 introduced the
use
field. It combines the ideas of oldloader
andloaders
fields into a single construct.
Webpack 2 引入了use
字段,它将之前的loader
和loaders
的功能合并成一个功能。 - Webpack 4 provides multiple ways to match and alter loader behavior. You can, for example, match based on a resource query after a loader has been matched and route the loader to specific actions.
Webpack 4提供了多种匹配和更改加载器行为的方法。例如,在匹配了加载器之后,可以基于resourcequery进行匹配,并将加载器路由到特定操作。
In the next chapter, you'll learn to load images using webpack.