[转载][译]JavaScript中的development模式怎么实现

原文链接:https://juejin.im/post/6844903917629734926

作者:Jess15088

===============================

实现 development和 production模式执行不同的代码,这依赖于你的JS代码编译流程(当然前提是你有JS编译流程……)。在Facebook,我们大概这样写代码:

if (__DEV__) {
  doSomethingDev();
} else {
  doSomethingProd();
}
复制代码

上面代码里,__DEV__不是一个真实存在的变量。它在JS代码编译阶段,会被一个常量来替换,通常在 development下是 true,在 production模式下是 false。在不同模式下,编译出来的最终代码长这样:

// In development:
if (true) {
  doSomethingDev(); // 👈
} else {
  doSomethingProd();
}

// In production:
if (false) {
  doSomethingDev();
} else {
  doSomethingProd(); // 👈
}
复制代码

在 production模式下,你通常会使用一些压缩工具(比如 terser)来压缩JS代码。同时,大多数的JS代码压缩工具,都会进行一些 死码消除,比如在生成的代码中,删除掉 if(false){}这样的代码分支。 因此,在 production模式下,经过压缩之后,最终产出的代码长这样:

// In production (after minification):
doSomethingProd();
复制代码

你在实际项目中,用到的可能并不是 __DEV__这个标记常量,如果你用的是 webpack这样的JS编译打包工具,那通常用的是另外一种常量标记方式,来实现这个功能。比如,在 webpack社区中,通常是这样来区分 development和 production的代码分支:

if (process.env.NODE_ENV !== 'production') {
  doSomethingDev();
} else {
  doSomethingProd();
}
复制代码

当你使用一些打包工具(比如webpack)来从 npm包的引入 ReactVue这样的类库时,这些类库里区分 development和 production的方式和上面这段代码是一样的(译注: 通过 process.env.NODE_ENV来区分不同模式,应该是 web前端开发中的一种约定了吧)。如果你是通过 <script>标签的方式,来直接加载已经提前编译好的版本,那么通常是以JS代码的文件后缀 .js和 .min.js来区分 development和 production模式的代码。

通过 process.env.NODE_ENV标记来区分 development和 production环境的约定,最初是来自于 Node.js。在 Node.js 中,有一个全局变量 process,并且可以通过 process.env这个对象来访问到代码执行时候的环境变量。但是,在前端代码(译注:指运行在浏览器里的那种JS代码)里,并不存在全局的 process变量🤯。

实际上,类似我们前面提到的 __DEV__process.env.NODE_ENV也是在代码编译阶段会被 development或者 production常量替换掉。替换之后的代码如下:

// In development:
if ('development' !== 'production') { // true
  doSomethingDev(); // 👈
} else {
  doSomethingProd();
}

// In production:
if ('production' !== 'production') { // false
  doSomethingDev();
} else {
  doSomethingProd(); // 👈
}
复制代码

从上面可以看出,替换之后, if里的表达式是不变的('production' !== 'production'的值永远都是 false),代码压缩工具能够移出掉对应的分支代码。最终 production模式下,压缩之后的代码是这样的:

// In production (after minification):
doSomethingProd();
复制代码

在 import和 export被加入JS语言标准之前的很多年,存在好几个相互竞争的JS模块化方案。Node.js 采用了来自 CommonJS的 require()和 module.exports

最初发布到 npm上的包,都是给 Node.js 使用的,并不是给前端代码用的。作为曾经是(或许现在也是?)最流行的 Node.js 服务端框架,Express使用NODE_ENV这个环境变量来开启 production模式。在这之后,其他的一些 npm包也采用了 process.env.NODE_ENV来区分不同环境。

像 browserify 这样的早期JavaScript代码打包工具希望能在前端工程中,引入 npm包提供的代码。(是的,那时候几乎没有前端开发者使用npm来发布自己的代码,你能想象到吗?) 。因此,这些早期的打包工具采纳了 Node.js 生态中已经广泛采用的约定,使用 process.env.NODE_ENV来区分不同环境。

从 React 发布的那天起,在提供预编译好的JS代码之后,React 还提供了对应的 npm版本。伴随着 React 的流行,越来越多的前端开发者也基于 CommonJS 的方式来发布前端代码到 npm 仓库。

React 需要在 production模式中移出掉 development模式下的一些代码。正好 Browserify 对这个问题有了解决方案,因此 React 也采用了这个方案,在 npm 包中通过 process.env.NODE_ENV来区分不同环境。到后来,越来越多的类库和工具,比如 Vue 和 webpack,都采纳了这个方案。

到2019年,browserify 已经不再流行。但是,在JavaScript代码编译阶段,将 process.env.NODE_ENV替换成 development或 production继续像之前一样被社区广泛采用。


还有一个事情,可能会让你困惑。在 React 的 Github 源码中,你会发现 __DEV__这样的环境标记。但是在 npm 上的 React 代码里,使用的却是 process.env.NODE_ENV。这是神马情况呢?

由于历史原因,为了和 Facebook 内部代码保持一致,我们使用了 __DEV__这个标记。在很长的一段时间里,React 代码都是直接被拷贝到 Facebook 的代码仓库,因此需要遵守相同的规则。因此,在发布到 npm 之前,我们会有一个替换步骤,将 __DEV__替换为 process.env.NODE_ENV !== 'production'

在一些场景下,这会导致问题。一段基于 Node.js 约定的代码,在 npm 下运行的很好,但是在 Facebook 内部却出错;或者是相反的问题。

因此,从 React 16 开始,我们做出了一些调整。针对每个环境(包括 <script>引入的预编译代码,npm,以及 Facebook内部仓库),我们都会 编译对应的bundle

这意味着,针对 React 中的源码 if (__DEV__),我们实际上针对每个包都会生成2个bundle:一个是设置了 __DEV__ = true编译出来的bundle;另一个是设置了 __DEV__ = false编译的bundle。

举个例子

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}
复制代码

这个入口JS,是惟一一处你的打包工具需要替换 process.env.NODE_ENV的地方,也是在这个入口JS,打包工具会忽略掉 development的 require

react.production.min.js和 react.development.js两个文件中,都 没有包含 process.env.NODE_ENV这样的检查代码。这很好,因为在 Node.js 里访问 process.env是 有些慢的。提前编译好两个模式下的 React 代码,也能让我们的最初JS文件大小,不管我们使用哪个打包工具,都 更加的一致



作者:Jess15088
链接:https://juejin.im/post/6844903917629734926
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值