为什么要区分环境
在开发网页的时候,一般都会有多套运行环境,例如:
- 在开发过程中方便开发调试的环境。
- 发布到线上给用户使用的运行环境。
这两套不同的环境虽然都是由同一套代码编译而来,但是代码内容却不一样,差异包括:
线上代码经过压缩处理
开发用的代码包含一些用于提示开发者的提示日志,这些日志普通用户不可能去看它
开发用的代码所连接的后端数据接口地址也可能和线上环境不同,因为避免开发过程中造成对线上数据的影响。
为了尽可能的复用代码,在构建的过程中需要根据目标代码要运行的环境而输出不同的代码。我们需要一套机制在源码中去区分环境。幸运的是webpack已经为我们实现了。
如何区分环境
具体区分方法比较简单,在源码中通过如下方式:
if(process.env.NODE_ENV === "production") {
console.log("你正在线上环境")
}else {
console.log("你正在使用开发环境")
}
其大概原理是借助于环境变量的值去判断执行哪个分支。
当你的代码中出现了使用process模块的语句时, webpack就自动打包进process模块的代码以支持非nodejs的运行环境。当你的代码中没有使用proces时就不会打包进process模块的代码。这个注入process模块作用就是为了模拟nodejs中的process,以支持上面使用的
process.env.NODE_ENV === 'production'
在构建线上环境代码时,需要给当前运行环境设置环境设置环境变量NODE_ENV == 'prodeuction',webpack的配置如下:
const DefinePlugin = require("webpack/lib/DefinePlugin")
module.exports = {
plugins: [
new DefinePlugin({
//定义NODE_ENV环境变量为production
"production.env": {
NODE_ENV: JSON.stringify("production")
}
})
]
}
注意在定义环境变量的时候使用的是JSON.stringify包裹字符串的原因是环境变量的值需要时一个由双引号包裹的字符串,而JSON.stringify('production')的值正好等于'"production"'.
执行构建后, 你会在输出的文件中发现如下代码:
if(true) {
console.log('你正在使用线上环境')
}else {
console.log('你正在使用开发环境')
}
定义的环境变量的值被代入到源码中, process.env.NODE_ENV === 'production'被直接替换成true。并且由于此时访问process的语句被替换了而没有了,webpack也不会打包进process模块了。
DefinePlugin定义的环境变量只对webpack需要处理的代码有效, 而不会影响nodejs运行时的环境变量的值。
通过shell脚本的方式去定义的环境变量,例如NODE_ENV=production webpack
,webpack是不认识的,对webpack需要处理的代码中的环境区分语句时没有作用的。
也就是说只需要通过DefinePlugin定义环境变量就能使上面介绍的环境区分语句正常工作,没必要又通过shell脚本的方式去定义一遍。
如果你想让webpack通过shell脚本的方式去定义的环境变量,你可以使用EnvironmentPlugin,代码如下:
new webpack.EnvironmentPlugin(['NODE_ENV'])
这句代码实际上等于:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
结合UglifyJS
其实以上输出的代码还可以进一步优化,因为if(true)语句永远只会执行前一个分支的代码,也就说最佳的输出应该直接是:
console.log("你正在线上环境")
webpack没有实现去除死代码的功能,但是UglifyJ可以做这个事情。
第三方库中的环境区分
除了在自己写的源码中可以有环境区分的代码外, 很多第三方库也做了环境区分的优化。以react为例,它做了两套环境区分:
- 开发环境: 包含类型检查, HTML元素检查等等针对开发者的警告日志代码;
- 线上环境: 去掉所有针对开发者的代码,只保留让react能正常运行的部分,以优化大小和性能。
例如react中有大量类似下面这样的代码:
if(process.env.NODE_ENV !== 'production') {
warning(false, '%s(...): Can only update a mounted or mounting component.... ')
}
如果你不定义NODE_ENV=production那么这些警告日志就会被包含到输出的代码中,输出的文件将会非常大。
process.env.NODE_ENV !== 'production'中的NODE_ENV和'production'两个值是社区的约定, 通常使用这条判断语句在区分开发环境和线上环境。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。