4、Webpack 环境配置:webpack.DefinePlugin 与 mode 配置,cross-env 跨平台设置
好,咱们接着聊环境配置。前面我们讲了怎么区分开发和生产环境,但真正落地到 Webpack 里,有两个东西你绕不开:mode 和 DefinePlugin。再加上跨平台的环境变量设置,这三板斧砍下去,你的多环境配置才算真正立住了。
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 |
| 构建速度 | 快(不做无用功) | 慢(要做压缩优化) |
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"
}
}
4.5 避坑指南
- 不要直接在代码里写死环境判断——比如
if (location.hostname === 'localhost')。这种写法既不安全,也不灵活。用 DefinePlugin 注入变量才是正道。 - 注意 DefinePlugin 的替换时机——它是在编译阶段替换的,不是运行时。所以如果你在代码里动态拼接字符串,比如
process.env['NODE_ENV'],DefinePlugin 是识别不了的。 - cross-env 不是万能的——它只能设置简单的键值对。如果你需要复杂的脚本逻辑,建议用
dotenv配合.env文件。
好了,这一章的内容就到这里。下一章我们会聊聊如何用 dotenv 管理多个 .env 文件,让环境配置更优雅。到时候见!