You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@babel/preset-env is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). This both makes your life easier and JavaScript bundles smaller!
The transform-runtime transformer plugin does three things:
Automatically requires @babel/runtime/regenerator when you use generators/async functions (toggleable with the regenerator option).
Can use core-js for helpers if necessary instead of assuming it will be polyfilled by the user (toggleable with the corejs option)
Automatically removes the inline Babel helpers and uses the module @babel/runtime/helpers instead (toggleable with the helpers option).
It wasn't possible to use @babel/preset-env's targets option with "pure" ponyfills, because @babel/plugin-transform-runtime is a completely separate package.
We forced our users to use core-js if they wanted a Babel integration. core-js is a good and comprehensive polyfill, but it doesn't fit the needs of all of our users.
困扰
作为工具开发者,babel 关联问题是难绕过去的砍。
在 babel@6 时候,最常收到反馈之一就是
regeneratorRuntime is not defined
而到了 babel@7,最常收到反馈之一
Cannot find module 'core-js/library/fn/**'.
那是什么问题导致这些问题的出现呢,我觉得有一个 issue 特别能代表这一类的开发者。
大家不要笑,我们内部一些基础模块也有这个问题
总结来讲:Babel 在编译大家的代码时候,会依据大家配置的 preset or plugin 注入一些模块依赖,而这些模块依赖是大家需要在
pkg.dependencies
里面体现出来的,否则很可能出现的问题就是加载不到具体的文件或者加载错误的版本的文件。根本的原因是什么:其实大家对
@babel/preset-env
@babel/plugin-transform-runtime
@babel/runtime
core-js
@babel/polyfills
babel-polyfills
等等这些熟悉但又陌生的原因。
那今天我想大概和大家分享一下使用
babel@7
的心得,如有不对,欢迎大家及时指出。预备知识
开讲之前我们有必要先来看看各个包到底是干啥的
@babel/preset-env
在
babel@7
推出之际,babel 官方把 babel preset stage 以及 es2015 es2016 等等都废弃了,取而代之的是@babel/preset-env
。通过官方文档的描述,preset-env 主要做的是转换 JavaScript 最新的 Syntax(指的是
const
let
...
等), 而作为可选项 preset-env 也可以转换 JavaScript 最新的 API (指的是比如 数组最新的方法 filter 、includes,Promise 等等)。这里细心的同学估计发现了我刻意在强调 Syntax 和 API, 是的,babel 在实现编译 或者我们在组合使用各个 preset 或者 plugin 时,其实有隐含这一层的关系的,同时这里也有一些历史背景,为了不给大家增加负担,我们只需要点到为止就好,需要了解的自行深挖也行。
说到这,我需要给大家一些 tips:
综上我想小伙伴们会有几个困惑
要解释清楚这几个问题,首先需要大概知道 preset-env 的三个关键参数
targets:
简单讲,该参数决定了我们项目需要适配到的环境,比如可以申明适配到的浏览器版本,这样 babel 会根据浏览器的支持情况自动引入所需要的 polyfill。
useBuiltIns:
"usage" | "entry" | false, defaults to false
这个参数决定了 preset-env 如何处理 polyfills。
false
: 这种方式下,不会引入 polyfills,你需要人为在入口文件处import '@babel/polyfill';
但如上这种方式在
@babel@7.4
之后被废弃了,取而代之的是在入口文件处自行 import 如下代码不推荐采用
false
,这样会把所有的 polyfills 全部打入,造成包体积庞大usage
:我们在项目的入口文件处不需要 import 对应的 polyfills 相关库。 babel 会根据用户代码的使用情况,并根据 targets 自行注入相关 polyfills。
entry
:我们在项目的入口文件处 import 对应的 polyfills 相关库,例如
此时 babel 会根据当前 targets 描述,把需要的所有的 polyfills 全部引入到你的入口文件(注意是全部,不管你是否有用到高级的 API)
corejs:
corejs
注意 corejs 并不是特殊概念,而是浏览器的 polyfill 都由它来管了。
举个例子
==Babel==>
这里或许有人可能不太清楚,2 和 3 有啥区别,可以看看官方的文档 core-js@3, babel and a look into the future
简单讲 corejs-2 不会维护了,所有浏览器新 feature 的 polyfill 都会维护在 corejs-3 上。
总结下:用 corejs-3,开启
proposals: true
,proposals 为真那样我们就可以使用 proposals 阶段的 API 了。总结
使用 preset-env 注入的 polyfill 是会污染全局的,但是如果是自己的应用其实是在可控的。
所以这里推荐业务项目这么使用
.babelrc
这样配置的原因是:
targets
下设置我们业务项目所需要支持的最低环境配置,useBuiltIns
设置为entry
,将最低环境不支持的所有 polyfill 都引入到入口文件(即使你在你的业务代码中并未使用)。这是一种兼顾最终打包体积和稳妥的方式,为什么说稳妥呢,因为我们很难保证引用的三方包有处理好 polyfill 这些问题。当然如果你能充分保证你的三方依赖 polyfill 处理得当,那么也可以把useBuiltIns
设置为usage
。针对大众普通项目,可能如上方式的配置(撇开个性化)应该够用了,
但追求极致的同学会有两个问题:
问题一:还是会有一定程度的代码重复,举个例子:
==Babel==>
_interopRequireDefault
这个方法,明显是可以变成一个独立模块,这样打包体积会变更小(再少也是爱)。问题二:针对项目,polyfill 会污染全局可以接受,但是作为 Library 我更希望它不会污染全局环境
两个都是好问题,那么接下来就是
@babel/plugin-transform-runtime
的出场机会了。@babel/plugin-transform-runtime
@babel/plugin-transform-runtime
官方描述是这样的
很明显该插件的出现就是复用 babel 注入的关联代码。
具体
@babel/plugin-transform-runtime
做了什么,官方也有明确的解释,相信大家都能看明白:举个例子:
这是不是解决了上面提到的问题一。
至于问题二,关于 polyfill 全局污染,不打算展开,因为涉及源码讲解,大家只需要知道 通过
@babel/plugin-transform-runtime
插件实现的 polyfill 是不会影响全局的,所以更适合 Library 作者使用另外也肯定会有好奇宝宝
@babel/plugin-transform-runtime
开启corejs
并且@babel/preset-env
也开启useBuiltIns
会咋样。结论是:被使用到的高级 API polyfill 将会采用runtime
的不污染全局方案(注意:@babel/preset-env
targets
设置将会失效),而不被使用到的将会采用污染全局的。所以总结下:
如果针对组件开发者,无需关注
@babel/preset-env
的targets
, 并开启@babel/plugin-transform-runtime
的corejs
,此时对于 polyfill 的注入相当于是@babel/preset-env
useBuiltIns
usage
的形式如果针对业务开发者,请开启
@babel/preset-env
的targets
,并关闭@babel/plugin-transform-runtime
的corejs
重要的问题讲三遍
根据如上 option,@babel/runtime 要做为项目的 dependencies
根据如上 option,@babel/runtime 要做为项目的 dependencies
根据如上 option,@babel/runtime 要做为项目的 dependencies
总结
@babel/plugin-transform-runtime
,建议关闭corejs
,polyfill 的引入由@babel/preset-env
完成,即开启useBuiltIns
(如需其他配置,自行根据诉求配置)。并在入口文件处 import 如下内容
@babel/plugin-transform-runtime
,建议开启corejs
,polyfill 由@babel/plugin-transform-runtime
引入。@babel/preset-env
关闭useBuiltIns
。但心细的同学,肯定又发现了新的问题
新的问题
为什么
@babel/preset-env
不能使用不污染全局的 polyfill(请注意不污染全局的 polyfill 必须由@babel/plugin-transform-runtime
引入);为什么要使用不污染全局的 polyfill 就必须要使用
@babel/plugin-transform-runtime
,而与此同时我必须妥协掉preset-env
targets
带来的体积优势(请注意,由于是不污染全局的前提,我们默认是由 runtime 引入 polyfill )如何解决呢?
抱歉在现有的 babel 正式体系下还没好办法来解决这个问题,当然 babel 也意识到了这个问题,于是有了
babel-polyfills
。注意是
babel-polyfills
不是@babel/polyfills
,我们移到文章最后。@babel/runtime
不需要深入研究它,请结合
@babel/plugin-transform-runtime
来看。core-js
corejs
不需要深入研究它,请结合
@babel/preset-env
来看。@babel/polyfills
已经在
babel@7.4 废弃
,请结合@babel/preset-env
来看。babel-polyfills
babel-polyfills
这个库的动机就是我们在
@babel/plugin-transform-runtime
小节下最后提出的问题:但是目前这个库处于
experimental
即试验性的阶段,按我对 babel 的了解,并不推荐大家当前在生产中引入,我们可以开放的心态保持关注即可。至于想要尝鲜的,官方也给了 升级方式。
ps: 自己尝鲜自己负责(我是求生欲极强的作者 o.o)
大总结
这篇文章没有 TLDR; 不管你是工具开发者、Library 开发者还是业务开发者,多一点耐心,好好把这篇文章捋一捋,因为就我观察 99% 的开发者都弄不明白。
「蚂蚁 RichLab 前端团队」致力于与你共享高质量的技术文章,欢迎关注我们的知乎/掘金专栏,将文章分享给你的好友,共同成长 :-)
我们团队正在急招前端工程师以及 22 届实习生:前端/全栈开发、互动图形技术、低代码/工程化、前端架构、数据算法 等技术方向任选。团队技术氛围好,上升空间大,简历可以直接砸给我 xuanji.jw@alipay.com
The text was updated successfully, but these errors were encountered: