你好,我是本文作者南一。如果有发现错误或者可完善的地方,恳请斧正,万分感谢!!
前言
之前使用babel一直搞不明白@babel/runtime core-js @babel/preset-env @babel/polyfill这几个包到底有什么区别,这次专门花费2天时间学习并记录下来。
背景
为了在旧时代的浏览器抢先体验新语法新特性,我们就需要借助babel的两个能力:语法转换和API支持。
语法转换
@babel/preset-env
语法转换能力由各种plugin支持,上述语法提案支持段落有列举,但是每个新语法都需要引入一个插件很不方便,因此@babel/preset-env出现了,它内部预设了所有的语法转换插件,一次引入即可畅享。
我们来测试一下,是否真的有这么神奇,我准备了一个🌰
// API
const a = Promise.resolve(1);
const b = new WeakMap([[a, 1]]);
Object.assign({ }, { a: 1 });
// 语法
const fun = async () => await a;
function* gennerateFun (){
yield a;
}
const obj = { a: 1, b: 2, c: 3 };
const { a: a1, b: b1, c: c1 } = obj;
for (let i of obj) {
console.log(i);
}
配置如下:
{
presets: [
["@babel/preset-env"]
],
plugins: []
}
.browserslistrc文件,设置目标环境为只能接受ES5语法,方便观察转译效果
safari >= 7
产出如下,我们注意到API都保留原状,语法都被转换了,符合预期。
此外,所有的语法转换函数、辅助函数都是直接在文件中定义,每个文件都重复定义,会增加代码体积,解决办法就是抽离为单独的文件,并且共享给每个文件。
@babel/runtime和@babel/plugin-transform-runtime就是为解决此问题而生。
@babel/runtime 和 @babel/plugin-transform-runtime
@babel/runtime 包含了babel在运行时需要的各种辅助函数,并且是模块化的。
@babel/plugin-transform-runtime 将babel使用到的辅助函数,替换成从@babel/runtime中引入。
测试一下,配置如下:
{
presets: [
["@babel/preset-env"]
],
plugins: [
["@babel/plugin-transform-runtime"]
]
}
产出如下,所有辅助函数都从@babel/runtime引入,符合预期
API支持
core-js
上面提到API都保留原状,这在低版本浏览器中还是不能运行,解决办法也有,我们可以基于现有的API和语法实现这些新API(polyfill),恰巧已经有人帮我们做了这件事:core-js
原本我们需要手动引入所需的polyfill,如下图,不过babel已经帮我们封装好了,只需要在@babel/preset-env的添加配置项useBuiltIns,即可自动引入polyfill。
useBuiltIns有三个选项:
"usage":按需引入core-js提供的polyfill"entry":在应用入口全量引入core-jsfalse:不转换也不引入任何polyfill
我们前面未给@babel/preset-env配置useBuiltIns,其实对应默认值false,因此API都保留原状
配置项useBuiltIns,需要配合配置项corejs一起使用,不然你会看到如下提示,它告诉我们需要指定core-js的版本。噢~,原来core-js发展到现在已经有很多个版本了,最新的是3版本它在2版本基础上又更新了很多功能。
接下来我们验证一下,配置如下,并手动安装npm i core-js@3
{
presets: [
["@babel/preset-env", {
useBuiltIns: "usage",
corejs: 3
}]
],
plugins: [
["@babel/plugin-transform-runtime"]
]
}
产出如下,果然,为了支持新API,从core-js中引入了很多函数
我们进入core-js的具体实现,可以看到所实现的API都是挂载在全局global,对应浏览器就是挂载window上面。这会带来什么问题呢?
如果我们是开发一个公用库,那别人一安装我们的包,这些polyfill就会污染宿主应用的全局环境。这时好奇的同学就要问了,那应该怎么办呢?其实啊,core-js已经帮我们考虑到了,它也提供了不污染全局环境的引入方式,如下:
同时,babel又又又帮我们封装好插件了,我们只需要安装npm i @babel/runtime-corejs3,并给@babel/plugin-transform-runtime添加配置项"corejs": 3即可,记得把前面在@babel/preset-env上的配置删除哦,不然仍然可能会引入全局的polyfill。
{
presets: [
["@babel/preset-env"]
],
plugins: [
["@babel/plugin-transform-runtime", {
corejs: 3
}]
]
}
产出如下,所有polyfill都用了别名引入,这样就不会污染环境啦!!!
@babel/polyfill
细心的同学就要问了,主播主播,@babel/polyfill这个包怎么没讲呢?好,我们到node_modules中看看怎么个事,原来@babel/polyfill依赖core-js@2和regenerator-runtime,可以理解为他俩的集合,但是现在已经不推荐使用了,变成版本弃子。现在可以使用@babel/runtime和core-js@2代替它。
结语
今天的分享就到这里啦,喜欢的伙伴可以三连支持一下,我是南一,我们下次再见,拜拜👋