组合 Webpack 配置

尽管 Webpack 还没有启用很多功能,但配置量开始变大。现在你必须要小心你的配置方式,因为你在项目中至少要区分生产和开发两个环境。随着功能的增加,情况只会变得更糟。

使用单个整体配置文件会变得不容易理解,并且可重用性几乎为 0。随着项目需求的增长,您必须找到更有效地管理 Webpack 配置的方法。

管理配置的方法

您可以通过以下方式管理 Webpack 配置:

  • 对于每一个环境都使用多个文件来组合配置,通过模块导入共享相同的配置,并通过 --config 参数将 Webpack 指向特定的环境。
  • 将配置打包成库,然后使用该库。例如:hjs-webpackNeutrinowebpack-blocks
  • 将配置转换成工具。例如:create-react-appkytnwb
  • 在单个文件中的维护所有的配置,并在其内部进行分支,使用传入 --env 参数来确定分支走向。本章后面将详细介绍该方法。

可以组合这些方法以创建更高级别的配置,然后由更小的部分组成。然后可以将这些部分添加到库中,然后通过npm使用它,从而可以在多个项目中使用相同的配置。

通过合并来组合配置

如果配置文件被分成不同的部分,则必须以某种方式再次组合它们。通常这意味着合并对象和数组。为了解决 Object.assignArray.concat 存在的问题,我们可以使用 webpack-merge

webpack-merge 做了两件事:拼接数组并合并对象,而不是简单的覆盖它们。以下示例详细显示了这种特性:

> merge = require("webpack-merge")
...
> merge(
... { a: [1], b: 5, c: 20 },
... { a: [2], b: 10, d: 421 }
... )
{ a: [ 1, 2 ], b: 10, c: 20, d: 421 }

webpack-merge 提供了许多可控的策略,使您能够控制每个字段的行为。它们允许您对于特定的字段进行追加,添加或替换内容。

在组合修改 Webpack 配置的时候,webpack-chain 提供了一个清晰的 API 简化这一过程。

设置 webpack-merge

首先,将 webpack-merge 添加到项目中:

npm install webpack-merge --save-dev

为了达到一定程度的抽象,您可以定义 webpack.config.js 管理一般用途的配置,而使用 webpack.parts.js 来管理特殊部分的配置。以下是从现有代码中提取的基于函数接口的部分配置:

webpack.parts.js

exports.devServer = ({ host, port } = {}) => ({
  devServer: {
    stats: "errors-only",
    host, // 默认为 `localhost`
    port, // 默认为 8080
    open: true,
    overlay: true,
  },
});

对于 stats 字段,这种方法也适用于生产环境,stats 的完整选项,请参阅官方文档

为了引入部分配置,请按照以下代码示例设置 webpack.config.js

webpack.config.js

const merge = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const parts = require("./webpack.parts");

const commonConfig = merge([
  {
    plugins: [
      new HtmlWebpackPlugin({
        title: "Webpack demo",
      }),
    ],
  },
]);

const productionConfig = merge([]);

const developmentConfig = merge([
  parts.devServer({
    // 如果有需要的话,可以在这里定义 host/port
    host: process.env.HOST,
    port: process.env.PORT,
  }),
]);

module.exports = mode => {
  if (mode === "production") {
    return merge(commonConfig, productionConfig, { mode });
  }

  return merge(commonConfig, developmentConfig, { mode });
};

注意 webpack.config.js 没有直接返回配置,而是返回了一个捕获 env 参数的函数。该函数根据参数返回配置,并将 env 参数映射到 webpack mode 上。这样做意味着 package.json 需要修改:

package.json

"scripts": {
	"start": "webpack-dev-server --env development", 
	"build": "webpack --env production"
}

完成这些更改后,功能和以前是一样的。但是,这一次,您有扩展的空间,您不必担心如何组合配置的不同部分了。

您可以根据需要扩展 package.jsonwebpack.config.js 的分支,而特定的配置可以放到 webpack.parts.js 中。通过这种方式,你可以做更多的事情,但配置依然条理清晰。

productionConfig 的规模还很小,随着我们进一步扩展配置,它将会增长。

代码中使用的 process 模块是 Node 中的一个全局变量。至于 env,它还提供了许多其他功能,使您可以获得有关主机系统的更多信息。

理解 --env

尽管 --env 只是允许将字符串传递给配置,但它还有更多的技巧。请考虑以下示例:

package.json

"scripts": {
  "start": "webpack-dev-server --env development",
  "build": "webpack --env.target production"
},

您应该可以在配置中接收 { target: "production" } 对象,而不是字符串。您可以传递更多的键值对,然后它们会转到该 env 对象下。如果在设置 --env.target 时又设置了 --env foo ,则字符串形式更加优先。Webpack 的命令行交互依赖于 yargs

组合配置的好处

配置拆分提供了更好的扩展性和复用性。你可以把通用型配置和特殊型配置提取成配置单元,把这些配置单元作为包在不同的项目之间复用。

您还可以将配置作为依赖项进行管理,而不是在多个项目中复制类似的配置。当您的配置升级或者问题修复时,您的所有项目都会同时受益。

每种方法都有其优点和缺点。基于组合的方法是一个很好的起点。就组合而言,每部分的代码量是有限的,很容易阅读。你还可以看看别人是怎么做的,从而找到一些更好的做法。

也许最大的问题在于,你需要知道自己在做什么,而且第一次配置可能会缺乏经验。但这就是一个软件工程问题,它不是 Webpack 所独有的。

您可以随时迭代优化配置单元或者找到更好的配置单元。通过传入配置对象而不是多个参数,您可以在不影响其 API 的情况下更改配置单元的行为,从而根据需要有效地公开配置单元的 API。

配置布局

在这本书中,您所有的配置将会分为两个文件:webpack.config.jswebpack.parts.js。前者包含通用型的配置,后者包含一些特殊配置,这样我们可以更方便的管理我们的配置文件。

按环境拆分配置

如果您按环境拆分配置,最​​终可能会得到如下文件结构:

.
└── config
    ├── webpack.common.js
    ├── webpack.development.js
    ├── webpack.parts.js
    └── webpack.production.js

在这种情况下,您将通过 Webpack --config 参数和 module.exports = merge(common, config); 来获得最终的配置环境。

按类别拆分配置

我们可以将特殊配置放到一个文件夹中,按照类别把 webpack.parts.js 拆分成一个个小的配置文件:

.
└── config
    ├── parts
    │   ├── devserver.js
    ...
    │   ├── index.js
    │   └── javascript.js
    └── ...

这种安排可以更快地找到与特定类别相关的配置。

将配置打包

鉴于所有配置都是 JavaScript,没有什么可以阻止您将其作为包使用。可以打包共享配置,以便您可以跨多个项目使用它。有关如何实现此目标的更多信息,请参阅维护手册

总结

尽管配置在技术上与以前相同,但是现在已经有足够的扩展性了。

回顾一下:

  • 鉴于 Webpack 配置是 JavaScript 代码,我们有很多方法可以管理它。
  • 您应该选择一种对您最有意义的方法来组合配置。webpack-merge 提供了一种轻量级的合成方法,但你还可以找到许多其他选项。
  • Webpack 的 --env 参数允许您通过终端控制最终的配置结果。在您的接口函数中会接受到这个参数。
  • 组合使得配置可以共享。您可以通过这种方式在仓库之间共享它,而不必在每个仓库都维护一个自定义配置。npm 包是一种不错的选择。开发配置和开发其他 JavaScript 代码是相似的。但是,这一次,您可以将您的实践成果打包成包。

本书的下一部分介绍了很多不同的技术,webpack.parts.js 会有许多变化。幸运的是,对 webpack.config.js 的更改仍然很少。

用户头像
登录后发表评论