webpack 5 配置多环境模式

1,332 阅读3分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

前言

在前端项目的开发、测试、上线等过程中,项目势必会运行在多种环境中,最少也有如: dev(开发)、pre(测试)、prod(正式)几种环境。也经常会遇到请求的接口也根据环境不同,提供了不同的url,如 pre 环境下使用 https://server-pre.com/api,prod 环境下使用 https://server.com/api

如果通过人力去对这些变量进行维护,那么不可避免的会有一些不方便的情况。那么我们需要一个 API_URL 的环境变量来进行一个判断,在对应环境下使用对应的接口连接。

对于 React 项目来说,实测并不能在 process.dev 获取到定义的 API_URL 内容。哪怕使用了 REACT_APP_API_ENV 的方式,也无法在项目中获取到这个环境变量。那么接下来,就需要升级一下我们的 webpack 配置了

webpack 配置可以参考:

我是这样搭建React+TS的通用webpack脚手架的(阶段一)

我是这样搭建React+TS的通用webpack脚手架的(阶段二)

定义 NDOE_ENV

刚开始我想到了通过定义 NODE_ENV 的形式来给 API_ENV 赋值,因此使用了 cross-env 的包来对环境变量进行定义

// 这是之前 package.json的 scripts
"scripts": {
    start: "webpack serve --config build/webpack.dev.config.js --open",
    build: "yarn build:css && yarn build:prod",
    build:prod: "npx webpack --config build/webpack.prod.config.js",
    build:css: "postcss src/css/styles.css -o src/css/output.css",
    watch:css"postcss src/css/styles.css -o src/css/output.css -w",
    dev"concurrently "yarn watch:css " "yarn start "",
    lint"eslint src --fix --ext .ts,.tsx "
}
//新的 package.json 的 scripts
"scripts": {
  "watch:css": "postcss src/css/styles.scss -o src/css/output.css -w",
  "build:css": "postcss src/css/styles.scss -o src/css/output.css",
  "start": "cross-env NODE_ENV=development webpack serve --config build/webpack.dev.config.js --open",
  "build:prod": "cross-env NODE_ENV=production npx webpack --config build/webpack.prod.config.js",
  "build:pre": "cross-env NODE_ENV=pre npx webpack --config build/webpack.prod.config.js",
  "dev": "concurrently "yarn watch:css " "yarn start "",
  "pre": "yarn build:css && yarn build:pre",
  "build": "yarn build:css && yarn build:prod",
  "lint": "eslint --fix src --ext .ts,.tsx "
},

此时,使用 yarn pre 命令进行打包,会发现一个报错: 5a495465977297c63a52c7a71894734.png 在看了 webpack 的文档后 NODE_ENV,也就是 webpackmode,只支持 none、development、production 这三种选项,如果需要自定义 mode,需要把配置从

module.exports = {
  mode: 'none',
};

修改到 function 的形式

const config = {
  entry: './app.js',
  //...
};

module.exports = (env, argv) => {
  if (argv.mode === 'development') {
    config.devtool = 'source-map';
  }

  if (argv.mode === 'production') {
    //...
  }

  return config;
};

那这就与我们之前在项目中根据 mode 拆分成 webpack.base.config.jswebpack.dev.config.jswebpack.prod.config.js 这些文件的初衷产生了冲突。

定义 API_ENV

既然设置 NODE_ENV 这种方式走不通,那就尝试在前端定义一个 API_ENV

更改一下 package.jsonscripts

"scripts": {
  "start": "cross-env API_ENV=dev webpack serve --config build/webpack.dev.config.js --open",
  "build:prod": "cross-env API_ENV=prod npx webpack --config build/webpack.prod.config.js",
  "build:pre": "cross-env API_ENV=pre npx webpack --config build/webpack.prod.config.js",
  "build:css": "postcss src/css/styles.css -o src/css/output.css",
  "watch:css": "postcss src/css/styles.css -o src/css/output.css -w",
  "pre": "yarn build:css && yarn build:pre",
  "build": "yarn build:css && yarn build:prod",
  "dev": "concurrently "yarn watch:css " "yarn start "",
  "lint": "eslint src --fix --ext .ts,.tsx "
},

这里用 webpackDefinePlugin 插件来修改一下 webpack.base.config

module.exports = {
    //...
    plugins: [
        //...
        new webpack.DefinePlugin({
          "process.env.API_ENV": JSON.stringify(process.env.API_ENV),
          API_ENV: JSON.stringify(process.env.API_ENV)
        })
    ]
}

然后在我们的 config.ts 文件中,修改我们的 apiUrl

let apiUrl: string;

const apiUrlMap: {[key: string]: string} = {
  dev: 'https://server-pre.com/api',
  pre: 'https://server-pre.com/api',
  prod: 'https://server.com/api',
}

apiUrl = apiUrlMap[process.env.API_ENV]

export default apiUrl;

结果

通过这种方式定义 API_ENV,我们就可以看到,部署在 pre 环境下的项目,使用的 apiUrlhttps://server-pre.com/api,production 环境下使用的是 https://server.com/api。这样,我们在每次合并 pre 分支的代码到主分支,就不用担心 apiUrl 被覆盖啦!