Webpack提供了多种设置模块 loader 的方法。Webpack 2 通过引入 use
字段来简化设置。使用绝对路径来定位 loader 是一个比较好的方式,这样你可以移动配置而不需做任何修改。
另一种方法是设置 context
字段,这会产生类似的效果并影响入口点和 loader 的解析方式。但是它对输出没有影响,你仍然需要使用绝对路径。
假设您设置了一个 include
或 exclude
规则,我们仍然可以从 node_modules 正常加载包,因为它们的编译方式使它们能够开箱即用。如果它们没有按照预期工作,那么您必须应用“使用包”章节中介绍的技术。
include
/exclude
在配合 node_modules 时非常方便。因为默认情况下 Webpack 在导入 JavaScript 文件时会遍历已安装的包。如果你要避免这种情况,那么你要配置这两个字段。其他文件类型不会受此问题的影响。
剖析 loader
Webpack 通过 loader 来支持各种格式的资源。此外,它本身支持一些 JavaScript 模块格式。在您的项目中,您需要按照目录结构,来设置一些 loader 并把它们连接起来。
考虑下面的例子,Webpack 通过 Babel 处理 JavaScript:
webpack.config.js
module.exports = {
...
module: {
rules: [
{
// 使用正则或者函数来匹配文件
test: /\.js$/,
// **限制**
// 限定需要匹配的目录
// 同样接受包含 path 的数组,或者返回 path 的函数
// exclude 的设置规则与此类似
include: path.join(__dirname, "app"),
exclude(path) {
return path.match(/node_modules/);
},
// 应用到匹配文件上的 loader
use: "babel-loader",
},
],
},
};
执行 loader
记住 loader 总是从右到左,从下到上依次执行的。把它们看成函数,从右到左的规则更容易记住。基于这样的规则,你可以把 use: ["style-loader", "css-loader"]
读为 style(css(input))
。
让我们来看一个例子:
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
根据从右到左的规则,保持等效的同时可以这样拆分示例:
{
test: /\.css$/,
use: "style-loader",
},
{
test: /\.css$/,
use: "css-loader",
},
强制调整顺序
尽管可以使用上述规则开发任意配置,但是可以定制一些特殊规则会更加方便。这时候,enforce
字段可以派上用场。它可以被设置为 pre
或 post
来所有 loader 之前或之后执行。
Linting 是一个很好的例子,因为在 lint 之前做任何构建都是不必要的。使用 enforce: "post"
是罕见的,通常你只是希望对源代码进行 lint,而不是构建结果。下面是对源代码进行 lint 的一个例子。
基本语法如下:
{
// 匹配条件
test: /\.js$/,
enforce: "pre",
// 执行的动作
use: "eslint-loader",
},
尽管我们可以根据 test 条件按照顺序小心地连接 loader,但使用 enfore
更加方便,它把 loader 的执行分成了几个不同的阶段,这是的 loader 的组合更加容易。
传递参数给 loader
有一种配置格式允许我们将参数传递给 loader:
{
// 条件
test: /\.js$/,
include: PATHS.app,
// 执行的动作
use: "babel-loader?presets[]=env",
},
这种配置风格也适用于入口和源代码导入。在某些情况下,这种格式会派上用场,但通常您最好使用更具可读性的替代方案。
最好的做法是通过 use
:
{
// 条件
test: /\.js$/,
include: PATHS.app,
// 执行的动作
use: {
loader: "babel-loader",
options: {
presets: ["env"],
},
},
},
如果您想使用多个 loader,可以将数组传递给 use
并从那里扩展:
{
test: /\.js$/,
include: PATHS.app,
use: [
{
loader: "babel-loader",
options: {
presets: ["env"],
},
},
// 在这里添加更多的 loader
],
},
通过函数在 use 中使用分支
之前,我们谈到一些组合配置的高级技巧。实现类似结果的另一个选择是在 use
中使用分支,因为 use
字段函数类型的值,而函数内部可以根据不同的环境条件返回不同的结果。请看以下示例:
{
test: /\.css$/,
// `resource` 有关资源路径匹配
// `resourceQuery` 可以包含一些传递给它的参数
// `issuer` 说明了执行上下文的路径
use: ({ resource, resourceQuery, issuer }) => {
// 这里你必须返回 falsy, object, 或者
// string (例如:"style-loader")
// 返回数组会失败,把它们包含到 rules 中
if (env === "development") {
return {
use: {
loader: "css-loader", // 首先是 css-loader
rules: [
"style-loader", // 然后是 style-loader
],
},
};
}
},
},
仔细应用的话,这种技术可以实现不同的组合方式。
内联 loader
尽管配置级 loader 的定义更常用,但可以内联编写 loader:
// 通过 url-loader 处理 foo.png, 其他匹配的 loader 也会执行
import "url-loader!./foo.png";
// 覆盖所有其他匹配的 loader, 并只通过 url-loader 处理 bar.png
import "!!url-loader!./bar.png";
这种方法的问题在于 Webpack 的特定规则污染了源代码。尽管如此,它仍然是一个很好的形式。由于入口也采用了相同的机制,因此这种写法在入口也适用:
{
entry: {
app: "babel-loader!./app",
},
},
匹配文件的其他方法
test
结合 include
或 exclude
是匹配文件的最常用方法。它们接受下面列出的数据类型:
test
- 匹配 RegExp,字符串,函数,对象或包含这些条件的数组。include
- 相同。exclude
- 相同,但匹配结果与include
相反。resource: /inline/
- 匹配特定路径的资源,包括参数部分。例如:/path/foo.inline.js
,/path/bar.png?inline
。issuer: /bar.js/
- 匹配该文件所访问的资源。例如:如果/path/bar.js
中引用了/path/foo.png
,那么后者就会被匹配。resourcePath: /inline/
- 匹配特定路径的资源,但是不包括参数部分。例如:/path/foo.inline.png
。resourceQuery: /inline/
- 根据参数匹配资源。例如:/path/foo.png?inline
。
这些字段还可以配合以下布尔值进行组合:
not
and
or
基于 resourceQuery 选择 loader
oneOf
字段可以根据匹配规则使用一个特定的 loader:
{
test: /\.png$/,
oneOf: [
{
resourceQuery: /inline/,
use: "url-loader",
},
{
resourceQuery: /external/,
use: "file-loader",
},
],
},
如果你想基于文件的路径名来确定 loader,你可以使用 resourcePath
。
基于 issuer 选择 loader
issuer
可用于根据资源的导入位置来确定是否应该该 loader。在下面改编自 css-loader issue 287 的示例中,当 Webpack 从JavaScript 导入中捕获 CSS 文件时,将应用 style-loader:
{
test: /\.css$/,
rules: [
{
issuer: /\.js$/,
use: "style-loader",
},
{
use: "css-loader",
},
],
},
另一种方法是混合使用 issuer
和 not
:
{
test: /\.css$/,
rules: [
// 从 CSS 模块以外导入的 CSS 会使用 style-loader
{
issuer: { not: /\.css$/ },
use: "style-loader",
},
// 而 CSS 模块内部导入的 CSS 会使用 css-loader
{
use: "css-loader",
},
],
}
理解 loader 行为
通过观察 loader 行为可以更详细地理解它们。loader-runner 允许您在没有 Webpack 的情况下单独运行它们。Webpack 在内部使用了这个包并进行了扩展,而 Loaders 扩展一章对此有详细的介绍。
inspect-loader 允许您检查 loader 之间传递的内容。您可以将此 loader 附加到您的配置并检查其中的流,而不必通过 console.log
来调试。
总结
Webpack 提供了多种配置 loader 的方法,但 use
在 webpack 4 中已经足够了。注意 loader 的排序,因为它是常见的问题来源。
回顾一下:
- loader 决定了 Webpack 在解析到不同文件的时候应该采取什么动作。
- loader 定义包括匹配条件,以及在该条件匹配的时候该采取的行动。
- Webpack 2 引入了该
use
字段。它将旧的loader
和loaders
字段的合并到了一起。 - Webpack 4 提供了多种方式来匹配和改变 loader 的行为。例如,您可以在
test
匹配后再基于查询参数进行匹配,将其引导到特定的 loader 操作上。
在下一章中,您将学习如何使用 Webpack 加载图像。