NODE_ENV是个什么?
类似于下面这样的代码大家或多或少都见过,总是见其形尚未思其义:
"scripts": {
"build": "cross-env NODE_ENV=production node build/webpack.prod.js",
"start": "cross-env NODE_ENV=development node build/webpack.dev.js"
},
其实,从技术的角度来讲,NODE_ENV其实就是由nodeJS暴露给执行脚本的一个环境变量,通常用来帮助我们在构建脚本中判断当前是devlopment还是production环境
上述脚本中的NODE_ENV变量会赋值给process.env对象
那么,问题又来了,process又是个啥玩意?
process是Node的一个全局变量,提供了当前Node进程的信息,它可以在脚本的任意位置使用,不需要通过require命令加载
process对象提供一系列属性,用于返回系统信息。
- process.argv:返回当前进程的命令行参数数组。
- process.env:返回一个对象,成员为当前Shell的环境变量,比如process.env.HOME。
- process.installPrefix:node的安装路径的前缀,比如/usr/local,则node的执行文件目录为/usr/local/bin/node。
- process.pid:当前进程的进程号。
- process.platform:当前系统平台,比如Linux。
- process.title:默认值为“node”,可以自定义该值。
- process.version:Node的版本,比如v0.10.18。
更多关于process的内容,此处不做过多讲解,请大家自行查看官方文档,点击进入直升机通道
process.env.NODE_ENV的作用
1.这个变量并不是 process.env 直接就有的,而是通过设置得到的。其实,更准确的来说是前端工程化过程中大家约定俗成的做法,尤其是webpack构建前端工程时,会经常使用。
2.其值通常为“production”(生产环境)和“development”(开发环境),或者“prod”和“dev”,以此来区分不同环境下的逻辑行为
if (process.env.NODE_ENV === 'development') {
//开发环境 do something
} else {
//生产环境 do something
}
那这个属性是什么时候赋值给process.env的呢?
以webpack的工程为例,通常是运行脚本时来做这件事,例如package.json中的脚本:
"scripts": {
"build": "cross-env NODE_ENV=production node build/webpack.prod.js",
"start": "cross-env NODE_ENV=development node build/webpack.dev.js"
},
说明:NODE_ENV=development在windows环境下会报错,需要改为set NODE_ENV=production,为了解决这个差异,可以使用cross-env跨平台的设置和使用环境变量,这里就不解释具体使用方法了。
这样,就可以在webpack.config.js中使用process.env.NODE_ENV了,但是要注意不能在webpack.config.js引入的模块中使用,要想在模块当中直接使用,我们还需要一些配置。
webpack4之前可以使用DefinePlugin插件配置
// webpack.config.js
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/app'
},
output: {
path: 'dist',
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) // 这里需要注意的是,值对应的格式必须是" "XXXX" "这种格式,所以会用 JSON.stringify 进行转换。
})
]
};
webpack4版本之后可以通过mode选项实现
module.exports = {
// 定义环境变量
mode: 'development',
// JavaScript 执行入口文件
entry: './main.js',
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
},
};
注意【关键点,勿混淆】:
通过npm script设置的NODE_ENV和通过DefinePlugin、mode选项定义的NODE_ENV是两个相互独立的变量
NODE_ENV=developmen这种方式定义的NODE_ENV只能在当前脚本中生效,是个runtime。
举个栗子:
例如webpack.config.js的mode我们设置为production,而脚本中执行NODE_ENV=development,那么在模块当中NODE_ENV的值为production,而配置文件webpack.config.js中的NODE_ENV的值为development
再举个栗子:
如果没有在npm script脚本中
设置NODE_ENV,但在webpack.config.js中设置了mode,那么我们在webpack.config.js中console出process.env.NODE_ENV的值就是undefined,因此如下代码可能就达不到我们想要的效果
let env = process.env.NODE_ENV === 'development' ? 'none' : 'production';
小结:
- DefinePlugin和mode选项定义的NODE_ENV 作用于webpack入口文件下的业务代码,通常为src文件夹下的代码
- 而 npm脚本里的设置多用于配置相关,例如在webpack.config.js里区分环境配置不同插件。
webpack4新特性mode
设置 mode ,可以让 webpack 自动调起相应的内置优化。
module.exports = {
// 可以是 none、development、production
// 默认为 production
mode: 'production'
}
或在命令行里配置:
"build:prod": "webpack --config config/webpack.prod.config.js --mode production"
那么,在设置了mode之后,webpack4 会同步配置 process.env.NODE_ENV 为 development 或 production 。
webpack4 支持零配置使用,这里的零配置就是指,mode 以及 entry (默认为 src/index.js)都可以通过入口文件指定,并且 webpack4 针对对不同的 mode 内置相应的优化策略。
1. production
配置:
// webpack.prod.config.js
module.exports = {
mode: 'production',
}
相当于默认内置了:
// webpack.prod.config.js
module.exports = {
performance: {
// 性能设置,文件打包过大时,会报警告
hints: 'warning'
},
output: {
// 打包时,在包中不包含所属模块的信息的注释
pathinfo: false
},
optimization: {
// 不使用可读的模块标识符进行调试
namedModules: false,
// 不使用可读的块标识符进行调试
namedChunks: false,
// 设置 process.env.NODE_ENV 为 production
nodeEnv: 'production',
// 标记块是否是其它块的子集
// 控制加载块的大小(加载较大块时,不加载其子集)
flagIncludedChunks: true,
// 标记模块的加载顺序,使初始包更小
occurrenceOrder: true,
// 启用副作用
sideEffects: true,
// 确定每个模块的使用导出,
// 不会为未使用的导出生成导出
// 最小化的消除死代码
// optimization.usedExports 收集的信息将被其他优化或代码生成所使用
usedExports: true,
// 查找模块图中可以安全的连接到其它模块的片段
concatenateModules: true,
// SplitChunksPlugin 配置项
splitChunks: {
// 默认 webpack4 只会对按需加载的代码做分割
chunks: 'async',
// 表示在压缩前的最小模块大小,默认值是30kb
minSize: 30000,
minRemainingSize: 0,
// 旨在与HTTP/2和长期缓存一起使用
// 它增加了请求数量以实现更好的缓存
// 它还可以用于减小文件大小,以加快重建速度。
maxSize: 0,
// 分割一个模块之前必须共享的最小块数
minChunks: 1,
// 按需加载时的最大并行请求数
maxAsyncRequests: 6,
// 入口的最大并行请求数
maxInitialRequests: 4,
// 界定符
automaticNameDelimiter: '~',
// 块名最大字符数
automaticNameMaxLength: 30,
cacheGroups: { // 缓存组
vendors: {
test: /[\/]node_modules[\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
},
// 当打包时,遇到错误编译,将不会把打包文件输出
// 确保 webpack 不会输入任何错误的包
noEmitOnErrors: true,
checkWasmTypes: true,
// 使用 optimization.minimizer || TerserPlugin 来最小化包
minimize: true,
},
plugins: [
// 使用 terser 来优化 JavaScript
new TerserPlugin(/* ... */),
// 定义环境变量
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
// 预编译所有模块到一个闭包中,提升代码在浏览器中的执行速度
new webpack.optimize.ModuleConcatenationPlugin(),
// 在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。
// 这样可以确保输出资源不会包含错误
new webpack.NoEmitOnErrorsPlugin()
]
}
2. development
配置:
// webpack.dev.config.js
module.exports = {
mode: 'development',
}
相当于默认内置了:
// webpack.dev.config.js
module.exports = {
devtool: 'eval',
cache: true,
performance: {
// 性能设置,文件打包过大时,不报错和警告,只做提示
hints: false
},
output: {
// 打包时,在包中包含所属模块的信息的注释
pathinfo: true
},
optimization: {
// 使用可读的模块标识符进行调试
namedModules: true,
// 使用可读的块标识符进行调试
namedChunks: true,
// 设置 process.env.NODE_ENV 为 development
nodeEnv: 'development',
// 不标记块是否是其它块的子集
flagIncludedChunks: false,
// 不标记模块的加载顺序
occurrenceOrder: false,
// 不启用副作用
sideEffects: false,
usedExports: false,
concatenateModules: false,
splitChunks: {
hidePathInfo: false,
minSize: 10000,
maxAsyncRequests: Infinity,
maxInitialRequests: Infinity,
},
// 当打包时,遇到错误编译,仍把打包文件输出
noEmitOnErrors: false,
checkWasmTypes: false,
// 不使用 optimization.minimizer || TerserPlugin 来最小化包
minimize: false,
removeAvailableModules: false
},
plugins: [
// 当启用 HMR 时,使用该插件会显示模块的相对路径
// 建议用于开发环境
new webpack.NamedModulesPlugin(),
// webpack 内部维护了一个自增的 id,每个 chunk 都有一个 id。
// 所以当增加 entry 或者其他类型 chunk 的时候,id 就会变化,
// 导致内容没有变化的 chunk 的 id 也发生了变化
// NamedChunksPlugin 将内部 chunk id 映射成一个字符串标识符(模块的相对路径)
// 这样 chunk id 就稳定了下来
new webpack.NamedChunksPlugin(),
// 定义环境变量
new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
]
}
3. none
不进行任何默认优化选项。
// webpack.com.config.js
module.exports = {
performance: {
// 性能设置,文件打包过大时,不报错和警告,只做提示
hints: false
},
optimization: {
// 不标记块是否是其它块的子集
flagIncludedChunks: false,
// 不标记模块的加载顺序
occurrenceOrder: false,
// 不启用副作用
sideEffects: false,
usedExports: false,
concatenateModules: false,
splitChunks: {
hidePathInfo: false,
minSize: 10000,
maxAsyncRequests: Infinity,
maxInitialRequests: Infinity,
},
// 当打包时,遇到错误编译,仍把打包文件输出
noEmitOnErrors: false,
checkWasmTypes: false,
// 不使用 optimization.minimizer || TerserPlugin 来最小化包
minimize: false,
},
plugins: []
}
小结
production 模式下给你更好的用户体验:
- 较小的输出包体积
- 浏览器中更快的代码执行速度
- 忽略开发中的代码
- 不公开源代码或文件路径
- 易于使用的输出资产
development 模式会给予你最好的开发体验:
- 浏览器调试工具
- 快速增量编译可加快开发周期
- 运行时提供有用的错误消息