正确使用Babel(1)后端

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

“babel 7”的图片搜索ç"“æžœ

我按照项目使用的目标环境分为了后端/library,前端/library,前后端都有的项目,这次我们先说后端/library。

后端项目

刚写了一个纯后端服务,先从后端说起,该服务依赖如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"scripts": {
"start": "nodemon ./src/index.js",
"build": "babel src -d dist -D -s true"
},
"dependencies": {
"knex": "^0.15.2",
"koa": "^2.6.2",
"koa-bodyparser": "^4.2.1",
"koa-router": "^7.4.0",
"mysql2": "^1.6.4",
"superagent": "^4.0.0",
"uuid": "^3.3.2"
},
"devDependencies": {
"@babel/cli": "^7.1.5",
"@babel/core": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"@babel/register": "^7.0.0",
"babel-plugin-module-resolver": "^3.1.1"
}

可以看到,所有的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
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
"presets": [
[
"@babel/preset-env", // 使用preset-env
{
"targets": {
"node": "current", // 目标版本
},
"useBuiltIns": "usage", // 是否polyfill
},
],
],
"comments": false // 去掉注释
}

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/polyfilluseBuiltIns选项配置成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
2
3
require("core-js/modules/es6.promise");

require("regenerator-runtime/runtime");

这时看到一条提示:

image-20190304101357553

说明使用了useBuiltins: usage已经不需要使用import '@babel/polyfill了,删除这句引入后我又在想,是否可以干脆干掉@babel/polyfill这个包呢,我们上次说过这个包现在包括corejsregenerator,而@babel/register带了core-js,那么还差regenerator-runtime,而升级到node10之后我们已经不需要regenerator-runtime,所以我觉得完全可以不再使用@babel/polyfill,而是直接使用 present-envuseBuiltIns: usage进行按需加载。

接下来我又尝试了更换@babel/runtime进行polyfill@babel/plugin-transform-runtime负责转换语法,@babel/runtime/@babel/runtime-corejs2负责提供polyfillcore-jsregenerator-runtime的引入是日常操作,对比@babel/polyfill,我们发现多了helperhelper可以帮助我们减少重复定义。(同时我观察到,既有polyfill(useBuiltIns),又有@babel/plugin-transform-runtime 的情况下,填充built-insrgenerator还是按照不污染全局的方式来的,而如果使用了实例的方法,结果还是污染了全局的变量,这部分对打包lib的转换影响大一些,这里不细说了,可以看下面lib的配置)

针对使用useBuiltIns没有helper的问题,我们可以使用@babel/plugin-external-helpers进行优化

  1. 安装 plugin,执行 babel-external-helpers 生成 helpers.js 文件,
1
2
3
npm install --save-dev babel-plugin-external-helpers

node_modules/.bin/babel-external-helpers > helpers.js
  1. 然后在 babel 的配置文件加入
1
2
3
{
"plugins": ["external-helpers"]
}
  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

带来的好处

1*uFKLXuq6bqU-7rgnOFdLlg.png如上图所说,babel实时编译产生的代码会缓存在内存中,导致内存占用过高。

总结