前面说了那么多,现在让我们来实践一下怎样是正确的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实时编译产生的代码会缓存在内存中,导致内存占用过高。