使用 loader

Webpack提供了多种设置模块 loader 的方法。Webpack 2 通过引入 use 字段来简化设置。使用绝对路径来定位 loader 是一个比较好的方式,这样你可以移动配置而不需做任何修改。

另一种方法是设置 context 字段,这会产生类似的效果并影响入口点和 loader 的解析方式。但是它对输出没有影响,你仍然需要使用绝对路径。

假设您设置了一个 includeexclude 规则,我们仍然可以从 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",
      },
    ],
  },
};

如果您不确定正则会如何匹配,请考虑使用在线工具,例如regex101RegExrRegexper

执行 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 字段可以派上用场。它可以被设置为 prepost 来所有 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 结合 includeexclude 是匹配文件的最常用方法。它们接受下面列出的数据类型:

  • 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",
    },
  ],
},

另一种方法是混合使用 issuernot

{
  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 字段。它将旧的 loaderloaders 字段的合并到了一起。
  • Webpack 4 提供了多种方式来匹配和改变 loader 的行为。例如,您可以在 test 匹配后再基于查询参数进行匹配,将其引导到特定的 loader 操作上。

在下一章中,您将学习如何使用 Webpack 加载图像。

用户头像
登录后发表评论