本文深入解析 Webpack 中环境变量的设置机制,重点区分 webpack.DefinePlugin 与 cross-env 插件的作用范围。DefinePlugin 用于在浏览器端注入全局变量,而 cross-env 仅用于 Node.js 构建环境。文章指出将两者混用可能导致变量覆盖或获取失败,并提供了正确的配置建议,帮助开发者避免常见的配置误区。
Webpack 环境变量配置基础
前言
- 日常的前端开发工作中,至少会有两套构建环境
- 在日常前端开发中,通常至少需要维护两套构建环境。一套用于本地开发调试,其构建结果不进行代码压缩,会打印 debug 信息并包含 sourcemap 文件;另一套用于线上发布,构建结果经过压缩,运行时不打印 debug 信息,且静态文件不包含 sourcemap。
- 一套发布时使用,构建结果用于线上,即代码都是压缩过的、运行时不打印 debug 信息、静态文件不包括 sourcemap 等
- 从 webpack 4.0 版本开始,官方引入了 mode 概念来简化环境区分。
| 选项 | 描述 |
|---|---|
| development | 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin |
| production | 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin |
区分开发/生产环境的方法
二、区分开发环境/生产环境的多种方式
2.1 使用命令行
2.1.1 写法一
"scripts": {
// 默认 mode 为 development
"dev1": "webpack-dev-server",
// 默认 mode 为 production,不过这样写,打包的时候会有警告
"build1": "webpack",
}
通过上述 script 脚本配置,可以在项目中的任意模块内通过 process.env.NODE_ENV 获取到当前的环境变量值。
然而,这种写法无法在 Node.js 环境(即 webpack 配置文件本身)中获取到当前的环境变量。
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// development | production
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
module.exports = {
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
...
};
2.1.2 写法二
"scripts": {
"dev2": "webpack --mode=development",
"build2": "webpack --mode=production",
}
该写法与写法一产生的结果完全一致。
2.1.3 写法三
"scripts": {
"dev3": "webpack-dev-server --env=development",
"build3": "webpack --env=production",
}
使用上述 script 脚本时,无法在任意模块内通过 process.env.NODE_ENV 获取当前的环境变量。
但是,可以在 Node.js 环境(webpack 配置文件)中,通过接收参数的函数来获取当前的环境变量。
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// undefined
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
// 通过函数获取当前环境变量
module.exports = (env,argv) => {
console.log('env',env);// development | production
return {
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
...
}
};
2.2 使用 mode
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// development | production
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
console.log('NODE_ENV',process.env.NODE_ENV);// undefined
module.exports = {
mode:'development',// development | production
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
...
};
其结果与 2.1 中的写法二相同。
两者的主要区别在于:前者是在命令行中设置,后者是在 webpack 配置文件中设置。
2.3 使用 webpack.DefinePlugin
首先需要明确该插件的作用:它用于设置全局变量(注意,并非挂载到 window 对象上),使得所有模块都能读取到该变量的值。
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// development
console.log(NODE_ENV);// production
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
// 这里只是凑巧和环境变量同名了,所以才不会报错
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// undefined
console.log('NODE_ENV',NODE_ENV);// error !!!
module.exports = {
mode:'development',// development | production
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
plugins:[
new webpack.DefinePlugin({
'process.env.NODE_ENV':JSON.stringify('development'),
'NODE_ENV':JSON.stringify('production'),
}),
],
...
};
在任意模块内,可以通过 process.env.NODE_ENV 获取到当前设置的环境变量。
同样,这种方式无法在 Node.js 环境(webpack 配置文件)下获取到当前的环境变量。
2.4 使用 cross-env 插件
此前我误以为该插件可以同时设置所有环境(包括浏览器环境和 Node 环境)下的变量。经过实际测试发现,它只能设置 Node 环境下的变量,即 NODE_ENV。
2.4.1 写法一
"scripts": {
"dev1": "cross-env NODE_ENV='production' webpack-dev-server",
"build1": "cross-env NODE_ENV='development' webpack",
}
通过上述 script 脚本,可以在任意模块内通过 process.env.NODE_ENV 获取到当前的环境变量。
同时,也可以在 Node.js 环境(webpack 配置文件)中获取到当前的环境变量。
但这里存在一个问题:在浏览器环境和 Node 环境下获取到的变量值可能不一致。
// !!!!!!
// npm run build1
// !!!!!!
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// production
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// development
module.exports = {
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
...
};
2.4.2 写法二
"scripts": {
"dev2": "cross-env NODE_ENV='development' --mode development",
"build2": "cross-env NODE_ENV='production' --mode production",
}
通过上述 script 脚本,可以在任意模块内通过 process.env.NODE_ENV 获取到当前的环境变量。
也可以在 Node.js 环境(webpack 配置文件)中,获取到当前的环境变量。
因此,在浏览器环境下读取到环境变量值主要依靠 mode 配置,而在 Node 环境下读取到环境变量值则依靠 cross-env。
// !!!!!!
// npm run build2
// !!!!!!
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// production
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// production
module.exports = {
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
...
};
常见配置误区解析
三、误区
经常在一些技术交流群中看到这样的疑问:当 cross-env 和 webpack.DefinePlugin 配合使用时,无法通过 process.env.xxx 来获取设置的环境变量,而必须通过 DefinePlugin 中设置的 key 来获取。
引发这个问题的可能原因是:先通过 cross-env 设置了 NODE_ENV 变量,随后又在 DefinePlugin 中重复设置了一遍环境变量。
"scripts": {
"dev": "cross-env NODE_ENV='development' --mode development"
}
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// development
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// development
module.exports = {
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
plugins:[
new webpack.DefinePlugin({
// 设置环境变量
'process.env.NODE_ENV':JSON.stringify('development'),
}),
],
...
};
此时会发现,浏览器环境和 Node 环境都能获取到设置的变量了,于是误以为之后设置其他全局变量时,只需照此写法即可。
认为 cross-env 和 DefinePlugin 是配套使用的,这种看法本身存在偏差,因为两者的作用点完全不同。
- DefinePlugin 的作用:是设置浏览器环境下能读取到的“全局变量”,直接通过 key 读取,在 Node 环境下是无法读取到的。
- cross-env 的作用:是通过命令行设置环境变量 NODE_ENV,使 Node 环境下能读取到,通过 process.env.NODE_ENV 读取。
- 如果在 DefinePlugin 里设置的 key 是 process.env.NODE_ENV,这会覆盖 webpack 通过 mode 模式设置的环境变量的值。
"scripts": {
"dev": "cross-env NODE_ENV='development' --mode development"
}
// index.js
function getEnv() {
console.log(process.env.NODE_ENV);// 666666
}
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
console.log('process.env.NODE_ENV',process.env.NODE_ENV);// development
module.exports = {
entry:'./src/index.js',
output: {
filename: 'js/[name].js'
},
plugins:[
new webpack.DefinePlugin({
// 注意:因为同名的 key,这里的值会覆盖默认的值 !!!!!!
'process.env.NODE_ENV':JSON.stringify('666666'),
}),
],
...
};
总结与建议
四、总结
cross-env 是专门用来设置 Node 环境变量的工具。
webpack.DefinePlugin 是专门用来设置浏览器环境下的全局变量(不会挂载到 window 上)的工具。
本文仅为个人理解,如有错误还请告知,万分感谢。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对本站的支持。