寻觅 webpack - 28 - 真实世界的 webpack - 配置多模式专案

本系列已集结成书从 0 到 Webpack:学习 Modern Web 专案的建置方式,这是一本完整介绍 Webpack 的专书,如有学习 Webpack 相关的问题可以参考此书。

本文讲解如何在同一专案中配置多种模式的 webpack 设定。

本文的范例程序放在 peterhpchen/webpack-quest 中,每个程序码区块的第一行都会标注档案的位置,请搭配文章作参考。

在开发专案时,会有两种配置:开发配置及生产配置。开发配置专责於开发阶段使用,使工程师得到更好的除错帮助,有意义的命名模组输出、 Source Map 、 Hot Module Replacement...等。而生产环境的配置则需要做最佳化、减少体积、切割代码以提高快取机会...等。但是这两种环境的配置也并不完全不同,其中对於模组的载入、入口的设定等在两个环境下是会相同的,这时要如何配置这些配置,又不会让使用者麻烦是需要特别注意的。

为解决此问题,本文会使用函式webpack-mergewebpack-chain 这三种不同的方式说明如何配置 webpack 的配置档。

使用函式设定配置档

使用 CLI 操作 webpack 中有提到,配置档可以用函式的方法设定,函式的参数会传入环境变数,以此来决定各个环境下的设定。

以下面的例子说明:

// ./demos/function/webpack.config.js
module.exports = (webpackEnv) => {
  const isEnvDevelopment = webpackEnv === "development";
  const isEnvProduction = webpackEnv === "production";

  return {
    mode: isEnvProduction ? "production" : isEnvDevelopment && "development",
    module: {
      rules: [
        {
          test: /\.js$/,
          use: {
            loader: "babel-loader",
          },
        },
      ],
    },
  };
};

我们可以在函式中组成目标环境的配置并传回,接着只要在指令中给予相应的环境变数,就可以使 webpack 依照不同的配置做建置的工作:

// ./demos/function/package.json
{
    ...
  "scripts": {
    "build": "webpack --env production",
    "dev": "webpack --env development"
  },
  ...
}

使用函式的好处在於只需要使用单一的配置档,利用 JavaScript 完全控制设定的输出,对於不同环境中各个设定不会相差太多时是个好选择,但如果环境间差距过大,最好还是避免使用此方法。

使用 webpack-merge 合并多个配置

使用多个 webpack 配置档设定不同环境的建置方式是最常见的做法,但大部分的配置不管差距再怎麽大,不同的环境还是会有相同的设定,为了避免重复的配置,开发者可以合并多个配置档的内容。

试想你有下面这些配置档:

root
|- /config
  |- /webpack.base.conf.js
  |- /webpack.dev.conf.js
  |- /webpack.prod.conf.js

你可能会想说使用 Object.assign() 或是 Spread Operator 做合并,但由於 webpack 配置属於多层结构,并且配置的顺序也有其特定的规则,因此尽量避免使用这样的方式做设定的合并处理。

为了安全的合并配置档内容,我们可以借助 webpack-merge 的帮助,它是专门为了合并 webpack 配置而开发的,可以完美的组合多个配置档。

以上面的例子来看,基底配置(webpack.base.conf.js)设定了关於模组的载入:

// ./demos/merge/config/webpack.base.conf.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};

而在开发(webpack.dev.conf.js)及生产配置(webpack.prod.conf.js)中使用 webpack-merge 与基底配置做合并:

// ./demos/merge/config/webpack.dev.conf.js
const { merge } = require("webpack-merge");
const baseWebpackConfig = require("./webpack.base.conf");

module.exports = merge(baseWebpackConfig, {
  mode: "development",
});

// ./demos/merge/config/webpack.prod.conf.js
const { merge } = require("webpack-merge");
const baseWebpackConfig = require("./webpack.base.conf");

module.exports = merge(baseWebpackConfig, {
  mode: "production",
});

接着在执行的时候针对不同的环境,选取不同的配置档:

{
    ...
  "scripts": {
    "build": "webpack --config config/webpack.prod.conf.js",
    "dev": "webpack --config config/webpack.dev.conf.js"
  },
  ...
}

这样的配置方式可以清楚的拆分各个不同的配置,对於专案中不同环境会有多种不同配置的情况十分的有用,开发者也能做很好的配置。

使用 webpack-chain 扩充配置

webpack-merge 足以应付大部分的需求,但是对於想要修改本来的规则时,例如说修改 babel-loader 中的 options 这样细部的变动,使用 webpack-merge 时必须将整个 babel-loader 的规则重写一遍,其中可能大部分的 options 都是相同的,这使得重复的代码产生。

需要这类细部的变动时可以使用 webpack-chain 来达成,它使用链式(chain) 设计 API ,用这些 API 我们可以产生合法的 webpack 配置,也可以使用 API 定位各个设定并做指定的修改。

现在我们修改 webpack-merge 的基底配置:

// ./demos/chain/config/webpack.base.conf.js
const WebpackConfig = require("webpack-chain");

const webpackConfig = new WebpackConfig();

webpackConfig.module
  .rule("js")
  .test(/\.js$/)
  .use("babel")
  .loader("babel-loader");

module.exports = webpackConfig;

在基底配置中我们设定了 babel-loader ,其中可以看到 webpack-chain 可以将各个设定做命名的动作:

  • .rule('js'):将此 rule 命名为 js
  • .use('babel'):将此 use(loaders) 命名为 babel

接下来在开发及生产配置档中就可以依照这些名字对应至想要修改的规则:

// ./demos/chain/config/webpack.prod.conf.js
const webpack = require("webpack");
const webpackConfig = require("./webpack.base.conf");

webpackConfig.module
  .rule("js")
  .use("babel")
  .loader("babel-loader")
  .tap((options) => options);

webpackConfig.mode("production");

module.exports = webpackConfig.toConfig();

// ./demos/chain/config/webpack.dev.conf.js
const webpack = require("webpack");
const webpackConfig = require("./webpack.base.conf");

webpackConfig.module
  .rule("js")
  .use("babel")
  .loader("babel-loader")
  .tap((options) => options);

webpackConfig.mode("development");

module.exports = webpackConfig.toConfig();
  • tap(): callback 中会传入之前设定选项 options ,修改後当作 callback 的回传值即可改变设定
  • toConfig(): 当要输出为合法的 webpack 配置物件时,使用此函式

webpack-chain 使用链式 API 让使用者可以完全控制并修改原有配置的所有细节,对於需要最高精细度修改的使用者来说非常合适。

总结

本文介绍了三种设定方式,各个特色如下表所示:

方式 例子
function 设定手法简单、拥有极高的控制能力 设定在单一档案中,配置混乱 facebook/create-react-app
webpack-merge 设定手法简单、高可读性 控制精细度较低 vuejs-templates/webpack
webpack-chain 链式 API 、控制精细度极高 配置方式非原生,必须额外学习 Vue CLI

参考资料


<<:  第二十九天:做一个总结吧

>>:  Day 28. 凭证绑定 Certificate Pinning 绑起来!

[Day 28] 来做一个人脸互动的程序吧!

在我完成人脸关键点与人脸对齐的学习後,觉得眼睛有点累想要休息 -- 这时一个应用就出来了! 我们每...

认识CSS(四):文字外观属性(css font)

用来控制网页文字的大小、粗细、样式、字型等效果,控制这几种文字外观的 CSS 属性分别是font-s...

Day 27:Google Map 范本学习(2)

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 Udemy 和 Youtu...

Day08:资料结构 - 堆积(Heap)

谈谈堆积(Heap)吧! 今天来谈谈堆积(Heap)吧!堆积是一种特别的二元树(Binary tre...

回顾

人的科技文明发展始终来自於人性 在这短短的一个月之中,说长也不到很长,经历了许许多的文章发文,这一些...