前面说了那么多,现在让我们来实践一下怎样是正确的babel使用方式,当然也不一定绝对正确,肯定还有些不足的地方,如果你看到了欢迎补充。

我按照项目使用的目标环境分为了后端/library,前端/library,前后端都有的项目,这次我们先说后端/library。
后端项目
刚写了一个纯后端服务,先从后端说起,该服务依赖如下:
1 | "scripts": { |
可以看到,所有的babel包全在devDependencies下,因为线上我们运行的是build之后的代码,我们简述一下每个包的作用:
- @babel/cli:命令行运行babel,为了build(构建)
- @babel/core:各种包的Peer Dependencies ,在7版本下几乎是一个必装的包
- @babel/preset-env:preset选env,没毛病
- @babel/register:开发环境使用,实时编译
- babel-plugin-module-resolver:唯一单独装的plugin,目的是设置常用包的别名(非必须)
再看下babel.config.js的配置(只看presets):
1 | module.exports = { |
node版本配置为current,这个后端项目我使用的是最新的LTS——v10.13.0,编译后发现代码差的不多,没有面目全非,主要是因为node版本高,所以不支持的语法已经不多。然后我把node目标版本换成8,发现没什么区别,直到我换成6,出现了_asyncToGenerator,说明在6版本,node还不支持async/await的语法糖,但是我启动项目发现可以运行,最后,我把target的node选项置为空,直接使用default的配置,这次终于运行不起来了。错误如下:
1 | ReferenceError: regeneratorRuntime is not defined |
可以看到是缺少regeneratorRuntime,要增加这个方法只能上polyfill了,参考我们上期写的,我们有三个选择,@babel/runtime、@babel/runtime-corejs2或者@babel/polyfill,任选其一即可,先试了一下@babel/polyfill,useBuiltIns选项配置成entry,终于运行没问题了(折腾完后在targets 为node 10/8环境运行正常,切换到node 6版本运行,发现koa-bodyparser这个包报错,查看源码,里面用了async function,而node_modules里的文件并没有转换,意味着这个包最低要求node 8版本了,实际上我们已经用10了,我们切换低版本只是为了检验babel中各个包的作用)。发现require了比较多的包进来,根据我们上次的总结,设置成entry会将polyfills拆分引入,仅引入不支持的polyfill,虽然相比不做任何处理已经有所优化,但这仍然不是我想要的,其实上次我们说过该配置还有一个选项是usage,可以做到检测代码中ES6/7/8等的使用情况,仅仅加载代码中用到的polyfills,这才是我们想要的,用上之后可以减少很大的代码量。我们可以看到配置了useBuiltIns: usage之后,真的实现了按需引用:
1 | require("core-js/modules/es6.promise"); |
这时看到一条提示:

说明使用了useBuiltins: usage已经不需要使用import '@babel/polyfill了,删除这句引入后我又在想,是否可以干脆干掉@babel/polyfill这个包呢,我们上次说过这个包现在包括corejs和regenerator,而@babel/register带了core-js,那么还差regenerator-runtime,而升级到node10之后我们已经不需要regenerator-runtime,所以我觉得完全可以不再使用@babel/polyfill,而是直接使用 present-env 的useBuiltIns: usage进行按需加载。
接下来我又尝试了更换@babel/runtime进行polyfill,@babel/plugin-transform-runtime负责转换语法,@babel/runtime/@babel/runtime-corejs2负责提供polyfill,core-js和regenerator-runtime的引入是日常操作,对比@babel/polyfill,我们发现多了helper,helper可以帮助我们减少重复定义。(同时我观察到,既有polyfill(useBuiltIns),又有@babel/plugin-transform-runtime 的情况下,填充built-ins和rgenerator还是按照不污染全局的方式来的,而如果使用了实例的方法,结果还是污染了全局的变量,这部分对打包lib的转换影响大一些,这里不细说了,可以看下面lib的配置)
针对使用useBuiltIns没有helper的问题,我们可以使用@babel/plugin-external-helpers进行优化:
- 安装 plugin,执行 babel-external-helpers 生成 helpers.js 文件,
1 | npm install --save-dev babel-plugin-external-helpers |
- 然后在 babel 的配置文件加入
1 | { |
- 入口文件引入 helpers.js
1 | require('./helpers.js'); |
实际上为了处理运行时问题,polyfill(useBuiltIns)和@babel/plugin-transform-runtime 我们只需选一个,区别在于polyfill会污染全局,这在我们自己项目里使用时是没有问题的,但是设计到开发一个library时就会导致污染使用者的全局变量,所以我们在自己的项目里使用polyfill(useBuiltIns: usage)(如果使用了@babel/register,那么就有了core-js,并且node版本在6以上,不再需要regenerator-runtime,可以直接不再使用@babel/polyfill,针对没有helper的问题,我们可以使用@babel/plugin-external-helpers进行优化),开发library时使用@babel/plugin-transform-runtime 。
日常使用时,并没有必要折腾这么多,服务放docker里,上最新的LTS加适当的配置(配置参考上面,我觉得已经足够简洁)就够了,现在大部分语法已经不需要太多转换了,如果不是npm包项目,直接选择present-env按需加载polyfill就可以,污染全局变量的问题在除了开发npm包之外的地方并不存在,并且如果corejs由@babel/register提供的话,@babel/polyfill都不需要了。
后端library
如果是library包,我们就要考虑全局变量污染的问题。因为如果你的包污染了使用者的全局变量的话,
babel-runtime:corejs2
babel-cli
后端编译执行
所谓后端编译执行就是在线上环境执行编译后的代码。
如何进行
https://juejin.im/entry/59f3e429518825295f5d3058
带来的好处
如上图所说,babel实时编译产生的代码会缓存在内存中,导致内存占用过高。