4、Webpack 环境配置:webpack.DefinePlugin 与 mode 配置,cross-env 跨平台设置

好,咱们接着聊环境配置。前面我们讲了怎么区分开发和生产环境,但真正落地到 Webpack 里,有两个东西你绕不开:modeDefinePlugin。再加上跨平台的环境变量设置,这三板斧砍下去,你的多环境配置才算真正立住了。

4.1 mode 配置:Webpack 内置的“聪明模式”

Webpack 4 之后引入了一个 mode 参数。说白了,它就是告诉 Webpack:“你现在是在开发环境,还是生产环境?”

我个人习惯在 webpack.config.js 里直接写死,但更好的做法是通过命令行传参。来看个例子:

// webpack.config.js
module.exports = {
  mode: 'development', // 或者 'production'
  // ...
};

你可能会问:就改个字符串,能有多大区别?

区别大了去了。我简单列一下:

特性 development production
内置优化 关闭压缩、开启命名 开启压缩、Tree Shaking
Source Map 默认 eval,调试友好 默认 none,保护源码
DefinePlugin 自动注入 process.env.NODE_ENV 自动注入 process.env.NODE_ENV
构建速度 快(不做无用功) 慢(要做压缩优化)
小提示: 如果你不设置 mode,Webpack 会默认走 production。我第一次用 Webpack 4 时就踩了这个坑——开发环境构建出来全是压缩代码,调试起来那叫一个痛苦。

4.2 DefinePlugin:在代码里“偷换”变量

DefinePlugin 是 Webpack 内置的一个插件。它的作用很直接:在编译阶段,把代码里的全局变量替换成你指定的值。

举个例子,你代码里写了:

if (process.env.NODE_ENV === 'production') {
  console.log('这是生产环境');
}

Webpack 编译时,会把 process.env.NODE_ENV 直接替换成字符串 'production'。然后 if 条件成立,保留这段代码。如果是开发环境,替换成 'development',条件不成立,这段代码就被 Tree Shaking 干掉了。

配置方式很简单:

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production'),
      'process.env.API_BASE_URL': JSON.stringify('https://api.example.com'),
    }),
  ],
};
注意: 值一定要用 JSON.stringify 包裹!因为 DefinePlugin 做的是文本替换,不是变量赋值。如果不加,替换后代码里会直接出现一个裸字符串,很容易报错。我曾经因为这个 bug 排查了整整一下午……

4.3 cross-env:跨平台的环境变量设置

好,现在问题来了。你在 package.json 的 scripts 里写:

{
  "scripts": {
    "build": "NODE_ENV=production webpack"
  }
}

这在 Mac 或 Linux 上跑得好好的。但换到 Windows 上,NODE_ENV=production 这种写法直接报错。Windows 用的是 set NODE_ENV=production

你总不能要求团队所有人都用同一套系统吧?

这时候 cross-env 就派上用场了。它是一个 npm 包,能帮你跨平台设置环境变量。

安装:

npm install --save-dev cross-env

使用:

{
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack",
    "dev": "cross-env NODE_ENV=development webpack serve"
  }
}

这样一来,不管你在哪个操作系统上跑,NODE_ENV 都能正确设置。嗯,这里要注意:cross-env 只负责设置环境变量,不负责读取。读取还是靠 process.env.NODE_ENV

4.4 实战:把它们串起来

说了这么多,咱们来个完整的例子。假设你有三个环境:开发、测试、生产。

// webpack.config.js
const webpack = require('webpack');
const path = require('path');

module.exports = (env, argv) => {
  const mode = argv.mode || 'development';
  const isProduction = mode === 'production';

  return {
    mode,
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: isProduction ? '[name].[contenthash].js' : '[name].js',
    },
    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(mode),
        'process.env.API_BASE_URL': JSON.stringify(
          isProduction ? 'https://api.example.com' : 'http://localhost:3000'
        ),
      }),
    ],
  };
};

然后在 package.json 里:

{
  "scripts": {
    "dev": "cross-env webpack --mode development",
    "build:test": "cross-env webpack --mode production --env test",
    "build:prod": "cross-env webpack --mode production"
  }
}
核心思路: mode 控制 Webpack 内置行为,DefinePlugin 控制代码里的变量,cross-env 保证跨平台兼容。三者配合,你的多环境配置就稳了。

4.5 避坑指南

  • 不要直接在代码里写死环境判断——比如 if (location.hostname === 'localhost')。这种写法既不安全,也不灵活。用 DefinePlugin 注入变量才是正道。
  • 注意 DefinePlugin 的替换时机——它是在编译阶段替换的,不是运行时。所以如果你在代码里动态拼接字符串,比如 process.env['NODE_ENV'],DefinePlugin 是识别不了的。
  • cross-env 不是万能的——它只能设置简单的键值对。如果你需要复杂的脚本逻辑,建议用 dotenv 配合 .env 文件。

好了,这一章的内容就到这里。下一章我们会聊聊如何用 dotenv 管理多个 .env 文件,让环境配置更优雅。到时候见!