webpack配置环境变量

3,969 阅读5分钟

在实际项目开发中,需要基于多种环境去设置不同的环境变量以便于在构建阶段或是运行阶段去使用,例如常见的通过process.env.NODE_ENV在构建时去判断当前的构建环境是development还是production,例如需要在开发环境测试环境生产环境去访问不同的接口服务器。

为了模拟真实的项目,使用webpack搭建了一个最小化的项目结构:

├─package.json
├─src
|  └index.jsx
├─public
|   └index.html
├─config
|   └webpack.config.js

Node环境变量

Node环境变量就是指process.env这个属性

process.env 是什么?

它是 Nodejs 应用程序中,process.env 属性,返回包含用户环境的对象,所以它不可以在客户端侧代码中使用,也就不能在浏览器环境上使用。

// process.env(官方示例)
{
  TERM: 'xterm-256color',
  SHELL: '/usr/local/bin/bash',
  USER: 'nodejscn',
  PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
  PWD: '/Users/nodejscn',
  EDITOR: 'vim',
  SHLVL: '1',
  HOME: '/Users/nodejscn',
  LOGNAME: 'nodejscn',
  _: '/usr/local/bin/node'
}

修改Node环境变量

1. 修改 package.json 文件

使用 cross-env依赖包,支持跨平台配置环境变量。

// package.json
{
  ...,
  "scripts": {
    "start": "npm run dev",
    "dev": "cross-env NODE_ENV=development AAA=123 webpack serve --config ./config/webpack.config.js",
    "build:test": "cross-env NODE_ENV=test  webpack --config ./config/webpack.config.js",
    "build:pro": "cross-env NODE_ENV=production  webpack --config ./config/webpack.config.js"
  },
  ...
}

通过在package.json脚本中设置变量的方式来注入环境变量,同时cross-env还支持去设置多个环境变量,只需要通过空格区分,例如在dev脚本中设置的NODE_ENV=developmentAAA=123

这样在执行npm start就能够通过process.env获取到对应的环境变量。

// webpack.config.js
console.log("【process.env】", process.env.AAA);

能够在构建时的终端中打印出

但是在index.jsx中也就是浏览器环境下的文件中打印process.env就会报错

原因就是前文提到的peocess.env是Node环境的属性,浏览器环境不能够获取到。让浏览器环境获取到所需变量我们后文再说。

2. 通过.env文件注入

直接通过在script脚本中注入环境变量的方式不利于集中管理环境变量,而且在环境变量较多时这种方式也十分不友好,所以需要一种方式来集中管理这些环境变量。

使用dotenv依赖包可将环境变量从 .env 文件加载到 process.env

dotenv会默认加载根目录的.env文件去注入环境变量,通过require('dotenv').config()即可完成注入。

//webpack.config.js
dotenv.config();

//.env文件
AAA=123

同样也能够在终端中看到

dotenv多环境配置

在多环境配置时需要通过规定不同环境对应的.env文件,例如现在规定.env.test是测试环境对应的环境变量,.env.production是生产环境,.env是开发环境。然后通过dotenv.config({ path: })去加载对应文件的环境变量。

//webpack.config.js
const PROJECT_PATH = resolve(__dirname, "../");

const dotenvFile = resolve(PROJECT_PATH, `./.env.${process.env.NODE_ENV}`);

// 加载.env*文件  默认加载.env文件
dotenv.config({
  path: fs.existsSync(dotenvFile)
    ? dotenvFile
    : resolve(PROJECT_PATH, `./.env`),
});

console.log("【process.env】", process.env.ENV);

这里process.env.NODE_ENV是为了判断当前的运行环境来去加载对应的.env文件。

// package.json
"scripts": {
  "start": "npm run dev",
  "dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.config.js",
  "build:test": "cross-env NODE_ENV=test  webpack --config ./config/webpack.config.js",
  "build:pro": "cross-env NODE_ENV=production  webpack --config ./config/webpack.config.js"
}

//.env.production
ENV=pro

//.env
ENV=dev

执行npm start

执行npm run build:pro

可以看到不同环境的变量确实已经注入成功。

浏览器环境变量

浏览器环境下也需要根据不同的环境变量来处理一些逻辑,但是它不能获取到process.env所以不能像注入Node环境变量的方式来实现。浏览器环境变量是基于webpack.DefinePlugin这个插件在项目构建时引入的,引入之后可以在前端代码中全局获取到对应的变量。

基础使用方式是将所需的变量按键值对的方式传入DefinePlugin中,需要注意的是变量值需要通过JSON.stringify进行包裹。

module.exports = {
  plugins: [
    new DefinePlugin({
        aaa: JSON.stringify("!!!!")
    })
  ]
}

执行脚本后可以在index.jsx中得到对应的结果

//index.jsx
console.log("【app】", aaa);
const App = () => {
  return <div>app</div>;
};

配置多环境的浏览器变量

浏览器环境中也需要根据项目环境来引入不同的变量,之前在Node环境中已经获取到不同的环境变量,我们可以建立一个基于此的映射表在项目构建时根据拿到的Node环境变量来引入对应的浏览器环境变量。

// webpack.config.js

// 浏览器环境注入的变量
const define = {
  dev: {
    baseURL: "/api/dev",
  },
  test: {
    baseURL: "/api/test",
  },
  pro: {
    baseURL: "/api/pro",
  },
};

module.exports = {
  new DefinePlugin({
    "process.env": Object.keys(define[process.env.ENV]).reduce((env, key) => {
      env[key] = JSON.stringify(define[process.env.ENV][key]);
      return env;
    }, {}),
  }),
}

执行npm start,可以在浏览器控制台看到结果

执行npm run build:pro,在dist目录开启一个服务器,可以在浏览器控制台看到结果

在实际项目中根据不同环境切换接口服务器地址的场景中,就能通过这样的方式来获取到不同环境中的接口地址。

在UMI中去配置多环境变量

在平时开发中较常使用umi作为项目框架,umi通过环境变量UMI_ENV区分不同环境来指定不同配置。

具体来说是通过脚本中注入的UMI_ENV=xxx去匹配对应的config.xxx.js配置文件,然后在define属性中去配置需要引入浏览器环境的变量。

具体相关内容可以看官方文档多环境配置代码中可用的变量

// webpack.config.js
module.exports = {
  ...,
  "scripts": {
    "start": "cross-env UMI_ENV=dev umi dev",
    "build:test": "cross-env UMI_ENV=test umi build",
    "build:pre": "cross-env UMI_ENV=pre umi build",
    "build:pro": "cross-env UMI_ENV=pro umi build",
  }
  ...,
}

//config.dev.js
import { defineConfig } from 'umi';

export default defineConfig({
  define: {
    'process.env': {
      BASE_API: '/api/dev',
    },
  },
});