Webpack构建优化——区分环境

为什么需要区分环境

在开发网页的时候,一般都会有多套运行环境,例如:

  1. 在开发过程中方便开发调试的环境。
  2. 发布到线上给用户使用的运行环境。

这两套不同的环境虽然都是由同一套源代码编译而来,但是代码内容却不一样,差异包括:

  • 线上代码被特殊压缩过。
  • 开发用的代码包含一些用于提示开发者的提示日志,这些日志普通用户不可能去看它。
  • 开发用的代码所连接的后端数据接口地址也可能和线上环境不同,因为要避免开发过程中造成对线上数据的影响。

为了尽可能的复用代码,在构建的过程中需要根据目标代码要运行的环境而输出不同的代码,我们需要一套机制在源码中去区分环境。 幸运的是Webpack已经为我们实现了这点。

如何区分环境

具体区分方法很简单,在源码中通过如下方式:

if (process.env.NODE_ENV === 'production') {
  console.log('你正在线上环境');
} else {
  console.log('你正在使用开发环境');
}

其大概原理是借助于环境变量的值去判断执行哪个分支。
当你的代码中出现了使用process模块的语句时,Webpack就自动打包进process模块的代码以支持非Node.js的运行环境。 当你的代码中没有使用process时就不会打包进process模块的代码。这个注入的process模块作用是为了模拟Node.js中的process,以支持上面使用的process.env.NODE_ENV === 'production'语句。
在构建线上环境代码时,需要给当前运行环境设置环境变量NODE_ENV = 'production',Webpack相关配置如下:

const DefinePlugin = require('webpack/lib/DefinePlugin');
module.exports = {
  plugins: [
    new DefinePlugin({
      // 定义 NODE_ENV 环境变量为 production
      'process.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需要处理的代码有效,而不会影响Node.js运行时的环境变量的值。
通过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没有实现去除死代码功能,但是UglifyJS可以做这个事情。

第三方库中的环境区分

除了在自己写的源码中可以有环境区分的代码外,很多第三方库也做了环境区分的优化。 以React为例,它做了两套环境区分,分别是:

  1. 开发环境:包含类型检查、HTML元素检查等等针对开发者的警告日志代码。
  2. 线上环境:去掉了所有针对开发者的代码,只保留让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'两个值是社区的约定,通常使用这条判断语句区分开发环境和线上环境。

你可能感兴趣的:(Webpack构建优化——区分环境)