前言

在上一篇文章——Babel配置不要再“复制粘贴”了,带你本人配一个Babel 中,咱们晓得:

  • 如果咱们开发的我的项目是应用程序,或者大型的我的项目,咱们能够利用@babel/preset-env的配置性能,对ES6+ API进行全局垫平。所以咱们个别这么配置:

    // babel.config.jsconst presets = [    [        '@babel/preset-env',        {            modules: false,            useBuiltIns: 'entry', // 或者 useBuiltIns: 'usage'            corejs: { version: '3.27.2', proposals: true }        }    ]];const plugins = [    '@babel/plugin-transform-runtime']module.exports = {plugins, presets};
  • 如果咱们是想开发一个第三方库,咱们能够利用@babel/plugin-transform-runtime的配置性能,对ES6+ API进行部分垫平,而不影响全局的环境。所以咱们个别这么配置:

    // babel.confing.jsconst presets = [    [        '@babel/preset-env',        { modules: false }    ]];const plugins = [    [        '@babel/plugin-transform-runtime',        {            corejs: {                version: 3,                proposals: true            }        }    ]];module.exports = {plugins, presets};

以上是咱们平时对于Babel见得最多的配置办法。然而这么配:

  • 有时咱们要关注@babel/preset-env配置,有时又要关注@babel/plugin-transform-runtime配置,并且配置项那么多,这种配置办法其实还是挺繁琐
  • Babel内容知识点原本就非常多,如果咱们自身对Babel就“懵懵懂懂”的话,这无疑会很容易加深咱们对Babel的纳闷,让咱们感觉更懵圈。

但其实还有一种新的Babel配置办法,它既能实现以上的配置,配置起来更加简洁,并且性能比下面的配置还要弱小,同时它也是Babel成员更举荐的配法

学习是个循序渐进的过程,所以这篇文章接下来会带大家:

  1. 大抵理解这种新的配置办法一些背景故事,为什么诞生
  2. 理解这种新的配置办法如何配置
  3. 答疑以前咱们学习对于Babel配置的疑难
  4. 目前新配置办法存在的问题

PS:

  • 为了大家浏览不便,每个模块绝对独立,大家能够筛选本人喜爱的模块浏览
  • 想间接看怎么用的敌人,能够间接跳到应用模块

特地阐明

本着写文章注重质量跟严谨性的态度,也为了大家能够更释怀的应用这个新的配置办法,因而为了这个新的配置办法,我也浏览了大量对于Babel的文章,也在github上提了一些issuesBabel的开发成员求证学习。

Babel的开发成员也非常敌对,也回应了我说:

  • 新的配置办法文档由两局部组成
  • 目前这个新的配置办法是他们更举荐的配法,齐全能够用来替换@babel/preset-env@babel/plugin-transform-runtime这两个包的配置性能,达到一样的配置成果
  • 新的配置办法有一个对应的包,这个包在@babel/preset-env外部也稳固应用了

具体的能够看看我在Babel提的issues

issues链接:

  • Some of the proposed methods do not seem to have been realised
  • The "useBuiltIns: usage" mode and "usage-global" mode have different output codes

目前Babel官网并没有对这个新的配置办法有更具体的阐明,置信后续这个办法可能也会成为Babel配置的支流。因而,大家学习到这种办法,也算是对Babel的一种拓展,然而最重要的是咱们能够利用这个新的办法,更不便的配置咱们的Babel

毕竟失去了官网Babel开发成员的答复与必定,所以这个新的配置办法,大家是能够释怀“食用”的

筹备

在文章开始之前,如果你对Babel曾经有一个零碎的理解,也明确Babel怎么配置,那么这篇文章会让你学习到Babel一种更弱小的新的配置办法;

然而如果你才刚接触Babel,或者对Babel似懂非懂,还处于迷迷糊糊的状态,那我还是强烈建议你先浏览之前的两篇文章(尤其是第二篇):

  • 想弄懂Babel?你必须得先弄清楚这几个包

    它次要介绍了咱们平时我的项目中应用Babel的几个包,并答疑了咱们学习Babel时的一些纳闷,让咱们对Babel有个零碎大抵的了解。

  • Babel配置不要再“复制粘贴”了,带你本人配一个Babel

    它次要解说了咱们平时我的项目中应用Babel配置的一些原理,还有应该如何更好的配置。

备注

  • 咱们把前言提到的以前的配置办法统称为:旧的配置办法
  • 咱们把这篇文章讲的新的配置办法统称为:新的配置办法
  • 咱们把通过引入core-js某个模块(或其余polyfill),来实现旧版本浏览器不反对的某个ES6+ API的过程,叫做垫平
  • 以后@babel/core最新版本是:7.21.8
  • 以后@babel/preset-env最新版本是:7.21.5

历史问题

通过前两篇文章,咱们晓得:

  • 咱们能够把所有这种寄存了ES6+ API办法与实现的汇合叫做polyfill
  • core-js只是一种polyfill,还有很多其余polyfill。例如: promise-polyfill、proxy-polyfill 、es-shims等
  • 如果咱们是用core-js这个polyfill来垫平的话,应该用core-js的最新版本来垫平,越新的版本,蕴含的ES6+ API实现就越多

OK,咱们接着往下看。

Babel我的项目中有这样一个包——babel-polyfills

留神:大家看清楚,不是@babel/polyfill这个包,它跟@babel/polyfill是两码事
不理解@babel/polyfill这个包的敌人,之前的文章——想弄懂Babel?你必须得先弄清楚这几个包中有专门的阐明

它的呈现,有一段故事,次要是为了解决以下问题(更具体的故事,能够看看这里):

  • 以前@babel/preset-env这个包的targets选项,不能与@babel/plugin-transform-runtime这个包共享,@babel/plugin-transform-runtime设置不了targets
  • 下面咱们说过,core-js只是一种polyfill,还有很多其余polyfill。所以有可能不是所有人都想用core-js这个polyfill
  • 为了更好的用户体验,旧的配置办法太繁琐

补充

对于问题一,咱们再做一些补充:

在旧的配置办法中,如果咱们想开发一个第三方库的话,咱们是利用@babel/plugin-transform-runtime的配置办法来开发的(因为配置@babel/plugin-transform-runtime,会以部分形式垫平ES6+ API)然而在以前,这会导致库的体积非常宏大。

这是因为之前@babel/plugin-transform-runtime(或者别的插件)不能辨认到咱们要兼容的指标环境(即targets,或者是packjsonbrowserslist),所以会把咱们代码中应用到的ES6+ API都垫平,即便咱们指标环境反对这些ES6+ API

然而在咱们后续学习Babel配置的时候,咱们会发现@babel/plugin-transform-runtime(或者别的插件)是能够辨认targets的,不能辨认targets这个问题并不能很好的领会到。这就让咱们非常纳闷。

如果你在学习Babel过程中对这个问题有过纳闷的话,能够去相干答疑模块查看起因

构造

babel-polyfills 能够看作是一个总的资源包,它寄存了三种用于垫平ES6+ API的插件包:

  • babel-plugin-polyfill-corejs2:用core-js 2+版本来垫平ES6+ API
  • babel-plugin-polyfill-corejs3:用core-js 3+版本来垫平ES6+ API
  • babel-plugin-polyfill-es-shims:用es-shims这个polyfill来垫平ES6+ API(为了解决上述问题二,不是每人都想用core-js这个polyfill来垫平)

OK,从下面对于三个包的形容,咱们能够分明的晓得它们的作用别离是什么。

咱们重点关注 babel-plugin-polyfill-corejs3 这个包,因为:

  • Babel举荐的polyfillcore-js,目前core-js最新版本曾经是3+的版本了
  • 我本人也试了一下用es-shims来垫平ES6+ API,然而并没有core-js那么不便(大家也能够本人试试)
  • 通过babel-plugin-polyfill-corejs3这个包的名字,以此类推,后续如果core-js降级到4+版本的话,应该会再出babel-plugin-polyfill-corejs4这个包等
  • babel-plugin-polyfill-corejs3这个包也曾经在@babel/preset-env外部应用了(相干链接)
  • 官网所说的新的配置办法,也是基于这个包

回顾

在解说如何应用新配置办法前,咱们先大抵回顾剖析一下旧的配置办法是怎么配置,不便后续咱们更好的了解如何应用。

在这里只是大略阐明,更具体的剖析,倡议大家去浏览上一篇文章——Babel配置不要再“复制粘贴”了,带你本人配一个Babel

抽象的来说,平时咱们做开发,不是开发大型项目、应用程序,就是开发第三方库。
而这些都是须要配置Babel来垫平咱们不反对ES6 API的指标环境的。所以在这里,咱们能够抽象的把垫平形式看成两大形式:

  • 全局形式垫平 —— 实用于大型项目或者应用程序
  • 部分形式垫平 —— 实用于开发第三方库

全局形式垫平

全局的形式垫平ES6+ API,则都是利用@babel/preset-env的配置性能,它有两种配法(配置代码可见前言模块):

  1. useBuiltIns: 'entry'
  2. useBuiltIns: 'usage'

useBuiltIns: 'entry'

这种形式比较简单粗犷。它须要咱们在入口文件手动import所有或者某块polyfill,这样Babel会引入咱们指标环境(targets)不反对的polyfill模块。

这种形式能够防止一些奇奇怪怪的问题,但某种程度来说,也会增大咱们打包后的体积。

useBuiltIns: 'usage'

这种形式则不须要咱们手动引入polyfillBabel会依据咱们以后代码中用到的ES6+ API,并判断以后的targets支不反对咱们用到的这个ES6+ API,不反对的话则主动导入这个ES6+ API对应的polyfill

这种形式尽管比拟不便,然而它是依据咱们以后代码中用到的ES6+ API来做判断的。所以如果咱们应用的第三方库中,有用到咱们以后targets不反对的ES6+ API,然而咱们本人的代码又没有用到这个API的话,那就会出问题了。

具体问题能够看看这个例子 use-third-party-library-problem

部分形式垫平

如果咱们开发的是一个第三方库,咱们当然要把咱们的代码与使用者的代码做隔离,所以咱们是须要以部分形式来垫平ES6+ API的。

而以部分的形式垫平ES6+ API,则是利用@babel/plugin-transform-runtime的配置性能(配置代码见前言模块)。

它会判断咱们想要兼容的指标环境(很久以前判断不了),并依据咱们代码中应用到的ES6+ API,最初再将polyfill局部变量的形式进行垫平。

应用

下面咱们的大略说了:

  • 新的配置办法诞生的一些历史
  • 新的配置办法是基于 babel-plugin-polyfill-corejs3 这个插件包
  • 回顾了一下旧的配置办法

咱们先来看看新的配置办法是怎么配置的,先有个大略印象:

const presets = [    '@babel/preset-env',]const plugins = [    [        'babel-plugin-polyfill-corejs3',        {            method: "entry-global", // usage-global || usage-pure            version: '3.20.2',            proposals: true        }    ],    '@babel/plugin-transform-runtime',];

咱们能够跟前言模块旧的配置办法比照一下,这几乎简便的不能再简便

OK,接下来咱们具体看看到底怎么配置。

配置项

在提了 issues 询问了对于文档的配置项,并失去Babel贡献者的答复后,能够晓得 babel-plugin-polyfill-corejs3 这个包有以下配置项:

  • method(重点)
  • version
  • proposals
  • targets
  • ignoreBrowserslistConfig
  • configPath
  • debug
  • include
  • exclude
  • shouldInjectPolyfill
  • absoluteImports
  • missingDependencies

大家不要被那么多配置项吓到,平时用到的就前三个targets也会解说一下),其余的在咱们须要用到的时候在学(太累了,能不学的尽量不学)。

method

这个配置项就是用来管制咱们垫平ES6+ API的形式。

它一共有三个值:

  • entry-global
  • usage-global
  • usage-pure

咱们能够这么拆解,这样更不便咱们记忆应用:

  • 咱们能够把-后面当做第一局部,它指的是管制polyfill引入的形式。是手动在入口引入,还是依据咱们代码中用到的ES6+ API主动引入
  • 咱们能够把-后面当做第二局部,它指的是polyfill是以全局还是部分的形式垫平
  • 咱们也会发现它们的值,跟旧的配置办法有所对应

entry-global

剖析

咱们能够把值拆成两局部来看:entryglobal。从这两个非常语义化的词咱们能够晓得:

  • entry入口。它指的是咱们要在入口引入咱们的polyfill
  • global全局。指的是,polyfill全局的形式垫平

咱们以ie 11为指标浏览器,用 entry-global 这个例子,来感受一下它跟旧的配置办法有没有什么区别:

依据下面的后果,咱们会发现:

  • 应用了新旧两种配置办法,发现编译后,注入的polyfill都是234行
  • 编译后的文件都是截然不同的。
总结

method: 'entry-global'相当于旧的配置办法中的useBuiltIns: 'entry'

留神

文档说version配置项,只有在应用usage-global或者usage-pure时才有用。

然而我试了一下,entry-global也是能够应用version,所以我有提 issues,目前没有回答

usage-global

剖析

咱们能够把值拆成两局部来看:entryglobal。从这两个非常语义化的词咱们能够晓得:

  • usage用法。它指的是咱们代码中用到ES6+ API
  • global全局。指的是polyfill全局的形式垫平

咱们以ie 11为指标浏览器,应用两个ES6+ API

  • array-grouping:它是个能够让数组分组更不便的API,目前它处于stage-3阶段
  • Promise:咱们最相熟的异步API

并且用 usage-global 这个例子来感受一下它跟旧的配置办法有没有什么区别:

依据下面的后果,咱们会发现:

  • ie 11中,只垫平了咱们用到的那两个ES6+ API
  • 编译后的文件都是截然不同的
总结

method: 'usage-global'相当于旧配置办法中的useBuiltIns: 'usage'

usage-pure

剖析

咱们能够把值拆成两局部来看:usagepure。从这两个非常语义化的词咱们能够晓得:

  • usage用法。它指的是咱们代码中用到ES6+ API
  • pure污浊。指的是polyfill部分的形式垫平,并不会净化全局,因而它是污浊的

咱们以ie 11为指标浏览器,还是应用下面两个ES6+ API

  • array-grouping:它是个能够让数组分组更不便的API,目前它处于stage-3阶段
  • Promise:咱们最相熟的异步API

并且用 usage-pure 这个例子来感受一下它跟旧的配置办法有没有什么区别:

依据下面的后果,咱们会发现:

旧的配置办法,是无奈垫平一些提案阶段的ES6+ API。因为旧的配置设置的version只能是33代表的是core-js@3.0.0的版本,那它必定不能垫平一些比拟新的提案

总结
  • method: 'usage-pure'相当于旧配置办法中,配置@babel/plugin-transform-runtime的形式
  • 新的配置办法比旧的配置办法更简便,更强
留神

咱们须要npm i core-js-pure -S,因为:

  • 全局变量形式垫平是基于core-js这个包
  • 局部变量形式垫平是基于core-js-pure这个包

文档没有说,不装置相干依赖的话,会报错

targets

它指的是咱们垫平ES6+ API指标环境

这个配置项设计的初衷是为了解决历史问题模块提到的第一个问题:

  • 利用@babel/plugin-transform-runtime配置开发第三方库时,以前@babel/plugin-transform-runtime是不能辨认targets的,这会导致库的体积很大。

所以有了targets这个配置项,会帮咱们大大的缩小体积。

咱们进行以下步骤:

  • 装置< 7.13.0版本的Babel@babel/core@7.12.0
  • 应用PromiseArray.includes两个ES6+ API

而后用 target-configuration 这个案例,感受一下以前targets这个配置项在以前是怎么发挥作用的。

咱们先看第一个后果(不配置targets,但设置了browserslist: ["chrome 100"]):

依照最新版本的Babel来说,设置了browserslist: ["chrome 100"],咱们的代码只会垫平chrome 100不反对的ES6+ API
然而当初咱们装置的是比拟旧的版本(7.12.0),依据上图咱们会发现,即便咱们设置的指标浏览器(chrome 100)是反对这些API的,但Babel仍旧垫平了这些API,所以咱们的库会变得非常大,这是历史问题模块问题一很好的体现。

咱们再来看第二个后果(配置targets):

咱们会发现,因为咱们新的配置办法设置了targets,所以此时Babel不会垫平targets反对的API,这大大节约了咱们库的体积。这就很好的解决了历史问题模块的问题一。

Babel >= 7.13.0的版本当前,在Babel配置文件新增了一个顶级的targets,并且Babel也反对辨认browserslist
所以在新的配置办法里targets这个配置项就不怎么用了。

version

它指的是咱们提供polyfill的汇合包(像core-js,外面寄存了很多polyfll汇合)的版本。

因为咱们是应用babel-plugin-polyfill-corejs3这个包

  • 全局形式垫平的话,是用core-js这个包来垫平,所以version指的是core-js版本
  • 部分形式垫平的话,是用core-js-pure这个包来垫平的,所以version指的就是core-js-pure的版本

咱们version当然是要越高越好,这样蕴含的polyfill才会更多

参考文章:babel-plugin-polyfill-corejs3

proposals

它指的是,咱们是否开启编译处于提案阶段的ES6+ API,那咱们当然是要开启的,这个值只有在method: 'usage-global' 或者 method: 'usage-pure'时有用

相干答疑

历史问题模块中,咱们有说到:

  • 在后续学习的过程中,咱们发现@babel/plugin-transform-runtime(或者别的插件)是能够辨认targets的,但有些文章说不能辨认,这加深了咱们对Babel的纳闷。

其实这所有的起因还是因为:咱们后续学习Babel的时候,Babel曾经进行了很多更新,咱们以后学习Babel时候的版本,跟以前那会的Babel版本曾经不一样了,因而问题不能很好的体现。

所以当前咱们学习Babel肯定要留神好版本号,这就是为什么对于Babel的文章,我都会写上Babel版本备注的起因。

OK,咱们接着往下看。

Babel公布7.13.0版本时候有一些记录说到:

  • 减少了顶层的targets
  • 反对辨认browserslist外面环境

这就是为什么后续咱们学习Babel,有些文章说@babel/plugin-transform-runtime不能辨认targets,然而咱们照着例子敲,后果却截然相同的起因。因为咱们学习的时候,Babel版本可能曾经>= 7.13.0,并且可能配置了browserslist或者targets,这时@babel/plugin-transform-runtime曾经能够辨认targets

为了更好的感触这个问题,咱们用 transform-runtime-targets-problem 这个案例来感受一下:

  • 咱们装置一个< 7.13.0版本(7.12.0
  • 配置browerslistchrome 100
  • 应用Arrayincludes办法(chrome 100曾经反对)
  • 应用Promisechrome 100曾经反对)

咱们来看看后果:

chrome 100曾经反对了ArrayincludesPromise,但仍旧被垫平了。

因为咱们装置的Babel < 7.13.0,此时还没实现插件能辨认targets

依据这个案例,置信大家应该对Babel的纳闷又缩小了一些了。

新配置办法目前存在的问题

咱们在回顾模块有说到,应用局部变量的形式垫平ES6+ API,它会依据咱们代码中应用到的ES6+ API,最初再以局部变量的形式进行垫平。

Babel在进行编译的时候,是会注入很多辅助函数的,那么会有这样一种状况:

  • 咱们的代码中没有应用到Promise,然而辅助函数中用到了Promise,那这时咱们的代码是不会垫平Promise的,因为咱们本人的代码中没有用到Promise,那这是不是会导致代码报错呢?

这是个很事实的问题,咱们用 usage-pure-problem 这个例子来看看新旧配置办法,对于这块问题有没有解决。

旧的配置办法

咱们先看看旧的配置办法的体现:

咱们来剖析一下这个编译过程:

  • 咱们的源码外面只应用了async
  • Babel@babel/runtime-corejs3这个包里引入了辅助函数
  • ie 11失常运行

新的配置办法

咱们先看看新的配置办法的体现:

咱们来剖析一下这个编译过程:

  • 咱们的源码外面只应用了async
  • Babel@babel/runtime这个包里引入了辅助函数
  • ie 11报错

剖析

咱们来剖析一下,为什么新的配置办法在ie 11会报Promise的错。

咱们本人的源码中没有用到Promise,所以这个Promise必定来自辅助函数。咱们能够看到新旧配置办法,引入的辅助函数是来自不同的包:

  • 旧的配置办法辅助函数来自:@babel/runtime-corejs3
  • 新的配置办法辅助函数来自:@babel/runtime

通过一番查问,咱们发现:

  • 旧的配置办法,Promise来自辅助函数的这个包:@babel/runtime-corejs3/helpers/asyncToGenerator
  • 新的配置办法,Promise来自辅助函数的这个包:@babel/runtime/helpers/asyncToGenerator

咱们比照一下这两个包有什么不一样的:

通过剖析比照,咱们能够很分明的晓得:

旧的配置办法,引入的辅助函数都是局部变量形式的存在的,所以咱们不须要放心咱们辅助函数里的ES6+ API没有被垫平;而新的配置办法则不是。

总结

所以,按目前状况来说,如果咱们想开发第三方库,并且想用新的配置办法来代替旧的配置办法:

  • 咱们要留神,这可能会导致在旧的浏览器运行不起来的状况。因为在新的配置办法中,辅助函数可能存在ES6+ API,它不会被垫平。
  • 新的配置办法目前来说,实用于咱们要兼容的一些比拟新的指标环境

以上问题当然也有别的开发者发现了,目前这个问题曾经解决了,预计后续的版本会修复。在修复当前,开发第三方库,咱们就能够完完全全的用新的配置办法代替旧的配置办法了。

参考文章:

  • @babel/plugin-transform-runtime not transform Object.hasOwn correctly
  • ownKeys, _objectSpread not imported from @babel/runtime helpers
  • Add back moduleName option to @babel/plugin-transform-runtime
  • Allow polyfill providers to specify custom @babel/runtime pkg

总结

OK,最初咱们总结一下新的配置办法:

  • 如果咱们开发的我的项目是应用程序,或者大型的我的项目,那咱们能够这么配置:

    // Babel配置const presets = [    [        '@babel/preset-env',        { modules: false }    ]];const plugins = [    '@babel/plugin-transform-runtime',    [        'babel-plugin-polyfill-corejs3',        {            method: "entry-global", // 或者method: "usage-global"            version: '3.20.2',            proposals: true        }    ]];module.exports = {plugins, presets};// package.json{    ...,    // 设置指标环境    "browserslist": [        "ie 11"    ]}// 入口文件// ---- useBuiltIns: 'entry'时,须要引入以下----// 垫平全副ES6+稳定版APIimport 'core-js/stable'; // ---- 或者 -----// 垫平所有ES6+ API,包含提案阶段import 'core-js';
  • 如果咱们是想开发一个第三方库,咱们能够这么配置:

    // Babel配置const presets = [    [        '@babel/preset-env',        { modules: false }    ]];const plugins = [    '@babel/plugin-transform-runtime',    [        'babel-plugin-polyfill-corejs3',        {            method: "usage-pure",            version: '3.20.2',            proposals: true        }    ]];module.exports = {plugins, presets};// package.json{    ...,    // 设置指标环境    "browserslist": [        "ie 11"    ]}// 入口文件const Method = {    wait(delay) {        return new Promise(resolve => setTimeout(() => resolve(), delay);    }}...

咱们能够比照一下旧的配置办法,新的配置办法更加的简洁,更加的弱小。咱们相当于:

  • 只利用@babel/preset-env进行ES6+语法编译
  • 利用babel-plugin-polyfill-corejs3来垫平ES6+ API
  • 咱们基本不须要在@babel/preset-env@babel/plugin-transform-runtime这两个包切来切去;咱们只须要关注babel-plugin-polyfill-corejs3如何配置就能够了

另外:

  • 如果想用新的配置办法用来开发第三方库,须要留神一些旧版本的浏览器可能会报错,因为它不会垫平辅助函数外面的ES6+ API。所以想开发第三方库,新的配置办法更适宜用于一些比拟新的浏览器
  • 如果想用新的配置办法用来开发利用我的项目或大型项目,用全局形式垫平ES6+ API,那么新的配置办法齐全能够间接代替旧的配置办法

最初

文章波及到的例子,曾经上传 Github,感觉有帮忙的话,欢送Star或者Fork学习(写文章真的很辛苦)。

如果读完这篇文章的你,感觉真的有帮忙到,欢送点赞珍藏;如果有异同点,欢送在评论区探讨