Babel配置不要再“复制粘贴”了,带你自己配一个Babel

lxf2023-02-17 01:52:40

前言

问题

我们在使用各种打包工具,需要配置Babel的时候,相信大家一开始都是直接在网上复制粘贴一段配置过来,然后能跑通就万事大吉了吧?因此,我们有时会遇到打包部署后,手机运行出现白屏问题;或者是,打包后代码包过大,加载缓慢等等问题。

其实这一切,大部分原因是因为我们对Babel各项配置没有一个系统的理解,所以即使从网上复制粘贴了配置,出现问题了,不知道怎么去分析出现的问题。

准备

如果你已经对Babel已经有一个大概的了解了,那阅读这篇文章,会让你对配置有一个更系统的了解;如果你才刚接触Babel,或者对Babel处于懵懵懂懂的状态,那我强烈建议你先阅读这篇文章——想弄懂Babel?你必须得先弄清楚这几个包,它主要介绍分析了一些概念跟以下几个包:

  • @babel/core
  • @bable/cli
  • @bable/preset-env
  • polyfill
  • @babel/polyfill
  • core-js
  • @babel/runtime
  • @babel/plugin-transform-runtime

并且为我们答疑了一些看官网时的疑惑。因为在清楚了这几包后,我们学习配置这块会更容易理解一些。

备注

  • 当前@babel/core最新版本是:7.20.12
  • 当前@babel/preset-env最新版本是:7.20.2

再谈core-js

通过上篇文章—— 想弄懂Babel?你必须得先弄清楚这几个包我们知道:core-js是一种polyfill,它提供了旧版本浏览器缺失的所有ES6+ API方法与实现。

在这里,以及下文,我们把通过引入core-js某个模块,来实现旧版本浏览器不支持的某个ES6+ API的过程,叫做垫平。

我们看看core-js这个包里面的主要一些模块:

Babel配置不要再“复制粘贴”了,带你自己配一个Babel

  • es:里面只包含有稳定的ES功能。
  • proposals:里面包含所有stage阶段的API
  • stable:它里面包含了,只有稳定的ES功能跟网络标准

所以,我们可以这么使用:

  • 当我们只需要垫平某个稳定的ES6+ API,我们可以用es这个文件夹里的polyfill来垫平 (import X from 'es/xx'
  • 当我们需要用到提案阶段API时,我就用proposals这个文件夹里的polyfill来垫平(import X from 'proposals/xx'
  • 当我们想垫平所有稳定版本ES6+ API,可以导入用stable文件夹(import 'core-js/stable'
  • 当我们想垫平所有ES6+ API(包括提案阶段),可以直接import 'core-js'

以上是我个人的使用习惯,因人而异,具体的介绍可以看看参考文章。

参考文章:core-js

再谈@bable/preset-env

通过上篇文章—— 想弄懂Babel?你必须得先弄清楚这几个包,我们知道:

  • Babel大体由两个功能组成:
    1. 编译ES6+最新语法letclass() => {}等)
    2. 实现旧版本浏览器不支持的ES6+APIPromiseSymbolArray.prototype.includes等)
  • @babel/preset-env有以下两个功能:
    1. 只编译ES6+语法
    2. 它并不提供polyfill,但是可以通过配置我们代码运行的目标环境,从而控制polyfill的导入跟语法编译,使ES6+新特性可以在我们想要的目标环境中顺利运行
  • @babel/plugin-transform-runtime也有以下两个功能:
    1. @babel/runtime@babel/plugin-transform-runtime两者配合,可以减少打包体积
    2. 也有一个配置功能,用来处理polyfill如何垫平
  • 如果我们想要在旧浏览器用到ES6+ API时,我们应该安装3版本的core-js(或者后续更高版本的);

那我们可以很清楚的知道:

  • 实现Babel第一个功能:我们用@babel/preset-env就可以了
  • 实现Babel第二个功能:我们就需要用core-js这个包来提供polyfill,并与@babel/preset-env或者@babel/plugin-transform-runtime的配置功能相互配合使用

我们先来看看@babel/preset-env的配置项有哪些:

// babel.config.js
const presets = [
    [
        '@babel/preset-env',
        {
            modules,
            targets,
            corejs,
            useBuiltIns,
            
            spec,
            loose,
            debug,
            bugfixes,
            include,
            exclude,
            forceAllTransforms,
            configPath,
            ignoreBrowserslistConfig,
            browserslistEnv,
            shippedProposals
        }
    ]
];
module.exports = {presets};

我们可以看到配置项还是蛮多的(有一些配置项,后期可能会废弃),但是,其实我们平时项目中主要用到前四个配置,所以在这里我们重点来看看前四个配置(能不学的尽量不学,太累了)。

参考文章:@babel/preset-env

modules

  • 功能:启用ES模块语法向另一种模块类型的转换
  • 默认值:auto
  • 可取的值:"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false

当我们设置成false的时候,Babel编译产生的一些辅助函数的引入方式会变成ES6模式引入(import A from 'B')。

我们把 use-transform-runtime 这个案例Babel配置改成以下配置,感受一下modules这个配置的功能。

// babel.config.js

const plugins = [
    '@babel/plugin-transform-runtime'
]

const presets = [
    [
        '@babel/preset-env',
        {
            modules: false
        }
    ]
];

module.exports = {plugins, presets};

没设置modules配置项时,编译后的文件是: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

我们会发现辅助函数都是以require的方式引入的;

设置了modules配置项后,编译后的文件是: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

我们会发现辅助函数变成了我们熟悉的ES6模块方式import引入。

这样有一个好处,就是我们用一些像Webpack打包工具时,可以对代码静态分析,很好地tree shaking减少代码体积,所以我们配置Babel的时候建议设置modules: false

参考文章:modules

targets

作用

它的用法与 browserslist 一致。它可以用来设置我们的代码需要兼容的目标环境,因此它:

  • 可以有效地减少ES6+的语法编译
  • 可以有效控制polyfill导入多少

注意

第一点

如果我们没有设置这个配置项时,它会去我们的Babel配置文件顶层的targets;如果顶层没有设置targets,则会去我们的package.json里的browserslist或者根目录找.browserslistrc;如果还没有则默认值为{}。查找过程大致如下,序号代表查找顺序:

// Babel配置文件
{
    targets: 'ie 10', // 2. 如果presets里面没有设置targets,会来这里查找
    presets: [
        [
            '@babel/preset-env',
            {
                targets: 'ie 9' // 1. 先在这里查找,没的话去顶层targets查找
            }
        ]
    ]
}

// package.json
{
    ...,
    browserslist: [
        'ie 11' // 3. 如果顶层targest里面没有设置,会来这里查找;如果这里也没设置,则为默认值{}
    ]
}

第二点

如果我们没有设置这个配置项时,Babel会假设我们要兼容的目标环境是最旧的浏览器,所以会将所有的ES6+语法代码转化为ES5所以我们配置Babel的时候,要设置targets以减少输出代码大小。

针对这点,我们用这个案例 preset-env-targets-config 来感受一下: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

我们会发现ES6+的写法全部被转成了ES5,还加入了一些辅助函数(白色框)。

ok,我们设置targets: 'chrome 80'。这表示,我们的代码是要在chrome 80上运行的,再看看打包后的结果:

Babel配置不要再“复制粘贴”了,带你自己配一个Babel

我们会发现编译出来的代码,跟我们入口文件写的代码基本没差。因为chrome 80已经实现了入口文件代码的写法了。所以,如果我们的代码不需要在一些比较低端的浏览器跑的话,设置targets就十分有必要。

参考文章:targets

corejs

useBuiltIns不为false的时候,需要设置这个配置项

配置

它有两种配置方式:

  1. 直接设置core-js版本号

    ...
    {
        useBuiltIns: 'usage',
        corejs: '3.27.2'
    }
    ...
    
  2. 配置corejs

    ...
    {
        useBuiltIns: 'usage',
        corejs: {
            version: '3.27.2',
            // 是否编译提案阶段ES6+ API
            proposals: false
        },
    }
    ...
    

注意

  1. 当我们的useBuiltIns不为false的时候,需要设置corejs这个配置项
  2. 2版本的core-js已经不建议使用了;我们用当然要用最新的,目前最新的版本是core-js@3.27.2
  3. 我们安装的core-js要尽量保持最新,因为越新的包,包含的polyfill才会越多
  4. 我们设置corejs的版本号时,不要直接指定2或者3,它会被解析为2.0或者3.0。所以,我们应该带上子版本号3.27.2),这样才会有最新的polyfill
  5. core-js默认用稳定版polyfill来垫平,但如果有时我们想用还处在提案阶段的API怎么办?
    • 如果我们配置的是useBuiltIns: entry,我们得手动引入core-js提案的polyfill来垫平。提案的polyfill放在core-js/proposals文件夹中import 'core-js/proposals/array-last'
    • 如果我们配置的是useBuiltIns: 'usage',则我们用上面说的corejs模块里面提到的第二种配置方式,把proposals设为true就可以了

它的作用会结合下面的useBuiltIns一起讲。

参考文章:targets

useBuiltIns

上面我们提到了,我们把通过引入core-js某个模块,来实现旧版本浏览器不支持的某个ES6+ API的过程,叫做垫平。

这个配置就是用来设置我们core-js的垫平方式的。它有下面三个值:

为了更好的理解,我们用Promise这个ES6+ API作为下面的例子。我们都知道:

  • IE 11并不支持Promise
  • Promise这个对象其实还有很多方法,例如Promise.anyPromise.allPromise.finally等。
  • 我们用这个案例 preset-env-useBuiltIns-config 来讲解

entry

我们可以这么理解,entry中文是“进入”的意思,这个值说明我们的polyfill应该是需要从某个入口引入来垫平。

表现

我们把配置设置为:useBuiltIns: 'entry'

  • 我们先来看看这个配置在IE 11的表现形式,我们设置targets: 'ie 11'Babel配置不要再“复制粘贴”了,带你自己配一个Babel

  • 我们再把targets设置成chrome: 80看看表现: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

分析

  • IE 11的表现

    我们import 'core-js/es/promise'(相当于import某块polyfill来垫平),由于我们的IE 11不支持Promise,所以useBuiltIns: 'entry'配置把我们所有不支持的Promise方法都垫平了(打印的window.Promise.any有值)。

  • chrome 80表现

    我们import 'core-js/es/promise'(相当于import某块polyfill来垫平), 因为在chrome 80中,Promise大部分方法已经实现,只有Promise.any没有实现,所以此时只垫平了promise.any方法。

总结

所以我们可以总结出,它的执行原理大致是这样的:

  1. 我们需要手动import所有或者某块polyfill
  2. Babel会根据我们设置的targets(目标环境),来判断我们手动import所有或者某块polyfill是不是当前缺失的
  3. 如果是的话,就会把我们手动import所有或者某块polyfill,拆分成很多小模块,引入我们目标环境不支持的模块

注意

  • 为了避免一些奇奇怪怪的问题,我们手动importpolyfill应该统一在入口文件(我们现在的工程项目一般都会有一个入口文件)
  • 如果我们想一劳永逸,直接把当前环境所有不支持的ES6+ API垫平,那我们就import 'core-js/stable'(这会垫平当前targets不支持的所有稳定版本的ES6+ API,所以也会导致包变大)
  • 如果我们只想单纯垫平某个ES6+ API(前提是targets不支持这个API,否则手动import了也没用;例如只想垫平Promise),那我们import 'core-js/es/promise就可以了
  • 如果想垫平提案阶段的API,则也需要手动import对应提案的polyfillimport "core-js/proposals/string-replace-all

参考文章:usebuiltins

usage

这个值的作用,就是我们不需要手动import,它会自动帮我们import当前targets缺失的polyfill

表现

我们把配置设置为:useBuiltIns: 'usage'

  • 我们先来看看这个配置在IE 11的表现形式,我们设置targets: 'ie 11'Babel配置不要再“复制粘贴”了,带你自己配一个Babel

  • 我们再把targets设置成chrome: 80看看表现: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

分析

  • IE 11的表现

    我们不用自己手动import相关polyfill,我们的代码中只用到了单一的Promise对象,所以只垫平Promise这个对象,不会垫平它其他相关的方法(打印的window.Promise.any无值)。

  • chrome 80的表现

    我们不用自己手动import相关polyfill,此时没有任何关于Promise的垫平,因为单一的Promise对象在chrome 80已经实现,且我们代码中没有用Promise.any方法,自然也就不会垫平Promise.any。此时代码只垫平了提案阶段的array.lastItem方法,因为chrome 80不支持它,并且我们代码中用到了它

总结

所以我们可以总结出,它的执行原理大致是这样的:

  1. 我们不需要手动import所有或者某块polyfill
  2. Babel会根据我们当前代码中用到的ES6+ API,并判断当前的targets支不支持我们用到的这个ES6+ API
  3. 如果不支持的话,则自动导入这个ES6+ API对应的polyfill

因为它会自动导入,所以我们专注写我们的ES6+就好了。

参考文章:usebuiltins

false

它是默认值。它表示不要在每个文件中自动添加polyfill,也不会根据targets判断缺不缺失,也不会将我们手动import所有或者某块polyfill拆分为单个polyfill引入。

表现

我们把配置设置为:useBuiltIns: false

  • 我们先来看看这个配置在IE 11的表现形式,我们设置targets: 'ie 11'Babel配置不要再“复制粘贴”了,带你自己配一个Babel

  • 我们再把targets设置成chrome: 80看看表现: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

总结

useBuiltIns设置为false:

  • 对我们的垫平方式没作用,源码是什么样,输出就是什么样
  • 设置targets无效

参考文章:usebuiltins

使用

适配 IE 11

相信通过上面的讲解,我们对各个配置项有个大概的了解了,那我们再更深入体验一下。

我们都知道IE 11基本是不支持ES6+的,我们抽几个常用的ES6+ API看看:

Babel配置不要再“复制粘贴”了,带你自己配一个Babel

那我们就用IE 11作为我们的targets(目标环境),研究一下如何配置Babel,使我们写的ES6+代码能在IE 11中跑起来。我们用这个案例 preset-env-template-config

注意: 为了避免Webpack打包错误,我们把targets写在package.json中,这样Webpack才能识别环境打包

...
"browserslist": [
   "ie 11"
 ]
 ...

我们用一下这段代码作为我们的入口文件:

// 如果用`usage`方式,则注释掉这段代码
import 'core-js/stable';

const lMC = {
    name: 'limingcan',
    like: ['eat', 'drink', 'play', 'fun'],
    breath() {
        return new Promise(resolve => {
            setTimeout(() => resolve(), 1000)
        })
    }
};

lMC.breath().then(() => console.log('breath'));
console.log(lMC.like.includes('play'));
console.log(Array.of(1, 2, 3));

根据我们上面几个配置项的讲解,如果我们想:

  • 一劳永逸的直接垫平targets所有不支持的ES6+ API,那我们的配置应该是:

    const presets = [
        [
            '@babel/preset-env',
            {
                modules: false,
                useBuiltIns: 'entry',
                corejs: {
                    version: '3.27.2',
                    proposals: true
                }
            }
        ]
    ];
    module.exports = { presets};
    

    此时IE 11正常输出:

    Babel配置不要再“复制粘贴”了,带你自己配一个Babel

    红色框是我们源码中没用到的ES6+ API,也都被垫平了;此时包的大小有115K

  • 如果我们想减少包的体积,只垫平我们用到的ES6+ API,那我们的配置应该是:

    const presets = [
        [
            '@babel/preset-env',
            {
                modules: false,
                useBuiltIns: 'usage',
                corejs: {
                    version: '3.27.2',
                    proposals: true
                }
            }
        ]
    ];
    module.exports = { presets};
    

    此时IE 11正常输出:

    Babel配置不要再“复制粘贴”了,带你自己配一个Babel

    但是我们会发现,红色框是我们源码中没用到的ES6+ API,它并没有被垫平的,它只会垫平我们代码中用到的API;此时包的大小只有29K

常见问题

通过上面的例子,我们会发现:

如果配置是useBuiltIns: 'entry'

  1. 我们垫平的polyfill,都是注入到window全局的,或者是某个内置对象的原型(prototype)上,这影响到了全局
  2. 一劳永逸的方式会垫平当前targets所有不支持的ES6+ API,这虽然方便,但这会导致包变得很大

如果配置是useBuiltIns: 'usage'

  1. 它也会将垫平的polyfill注入到全局。
  2. 由于useBuiltIns: 'usage'是判断当前targets支不支持我们代码中用到的ES6+ API,如果不支持会自己import
    那有这样一种情况,如果第三方库用到了我们当前targets不支持的ES6+ API,但我们自己的代码没有用到这个API,那么这个API是不会被叠平的,这会导致我们项目报错

第三方库问题

针对useBuiltIns: 'usage'配置的第二点弊端,我们来看这个例子 use-third-party-library-problem 更直接的感受一下。

我们把targets设为IE 11,我们自己的代码只用includes这个ES6+ API,然后再用第三方库 tars-utils 的optimizeImage方法,生成一个Blob对象(里面是用Promise实现的)。

我们在IE 11跑一下看看:

Babel配置不要再“复制粘贴”了,带你自己配一个Babel

通过Babel编译后的文件,我们会发现:

  • 只有我们代码中使用的includes方法被垫平;
  • 第三方库中用到的Promise并没有被垫平,所以导致IE 11报错了。这是一个非常现实的问题。

我们在高级一点的浏览器(chrome 108)看看:

Babel配置不要再“复制粘贴”了,带你自己配一个Babel

chrome 108运行没有任何问题,因为chrome 108已经内部实现了Promise

总结

看到这里,相信我们对@babel/preset-env的配置功能已经有个大概的了解了。上述所说的问题确实是我们很实际的问题。

因此,useBuiltIns设置entry还是usage,还是得根据我们项目实际需求来,我个人建议:

  • 由于我们@babel/preset-env配置方式,是会把当前targets缺失的API注入到全局,所以这个配置方式是比较适合我们做的一些前端工程项目或者应用程序。如果对包的体积不是很在意,建议使用useBuiltIns: entry的方式,然后再import 'core-js/stable',一劳永逸的垫平,这样可以帮我们避免掉一些奇怪的问题,
  • 如果使用useBuiltIns: usage,那还是得注意第三方库的问题。如果判断出哪块polyfill缺失,我们可以自己手动import去垫平

再谈@babel/plugin-transform-runtime

上面说到,@babel/preset-env配置方式,是会把当前浏览器缺失的API注入到全局的,那么有没有不注入全局的办法呢?答案是有的。它就是我们接下来要讲的@babel/plugin-transform-runtime配置。

我们先来看看它有几个配置项(谢天谢地,不算很多):

// babel.config.js
const plugins = [
    [
        '@babel/plugin-transform-runtime',
        {
            helpers,
            regenerator,
            corejs,
            
            version
        }
    ]
];
module.exports = {plugins};

helpers && regenerator

解析

我们先看前两个配置,因为前两个配置比较简单,我们放在一起讲。

  • helpers默认值是:true
  • regenerator默认值是:true

通过上篇文章——想弄懂Babel?你必须得先弄清楚这几个包,我们知道:

关于@babel/runtime

  • @babel/runtime存放了Babel辅助函数的一个集合包

关于辅助函数

  • 当我们只用了一些ES6+语法糖的时候,Babel编译时会内联注入一些辅助函数
  • @babel/plugin-transform-runtime@babel/runtime配合使用时,会将我们用到的辅助函数,从@babel/runtime中以require或者import的方式,引入到我们的文件中,实现复用,从而减小我们最终输出包的体积。

关于regenerator

  • 我们的源码里面使用了async function() {}等异步函数,或者fuction* myGenerator() {}这种Generator函数的话,就会需要用到regenerator-runtime这个包来编译。
  • Babel >= 7.18.0regenerator-runtime包里的内容,会以局部变量的方式内联注入到我们的代码中,这样我们就不需要全局提供一个regeneratorRuntime对象。

例如我们使用了class语法糖跟async function() {},然后用@babel/runtime@babel/plugin-transform-runtime(默认情况下,helpersregenerator值为true)配合使用,编译后如下图: Babel配置不要再“复制粘贴”了,带你自己配一个Babel


当我们把helpersregenerator的值设为falseBabel配置不要再“复制粘贴”了,带你自己配一个Babel

我们会发现我们的辅助函数,跟regenerator-runtime这个包又变回了内联方式。所以:

  • helpers:控制的是我们的辅助函数,是否不内联进我们的代码中。
    • true的话是不内联,而是引用@babel/runtime辅助函数集合包
    • false的话,则会内联
  • regenerator:与helpers类似,控制的是我们regenerator-runtime这个包的代码,是否不内联进我们的代码中。
    • true的话是不内联,而是引用@babel/runtime辅助函数集合包
    • false的话,则会内联

注意

官网中关于regenerator的解释,大致意思是: regenerator如果设为false,需要我们提供一个全局的regeneratorRuntime的对象。

但是:

  • Babel >= 7.18.0以后,regenerator-runtime包里的内容,会以局部变量的方式内联注入到我们的代码中。所以其实Babel >= 7.18.0 regenerator这个配置项个人觉得是基本没用了
  • Babel < 7.18.0的话,则需要我们提供一个全局的regeneratorRuntime的对象。相关案例可查看 import-regenerator-runtime,开启babel.config.js相关注释代码。

corejs

解析

corejs这个配置项一旦不为false,就是用来设置我们的要垫平的ES6+ API,以不污染全局局部变量方式垫平。

它有三个值:

对应依赖补充
false(默认值)@babel/runtime
2@babel/runtime-corejs21. 只能编译支持全局变量(如Promise)和静态属性(如Array.from);
2. 不能编译实例相关方法([].includes
3@babel/runtime-corejs31. 既能编译能编译支持全局变量和静态属性,又能编译实例方法
2. 开启proposals: true,还可以编译提案阶段的API

我们用这个例子 transform-runtime-config 体验一下。

  • 如果配置的是corejs: 2的话,会怎么样: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

    我们会发现,实例方法includes没被垫平,提案APIMath.signbit)也没有被垫平;Promise是以局部变量的方式出现在我们的代码中。

  • 我们再看看配置的是corejs: {version: 3, proposals: true}Babel配置不要再“复制粘贴”了,带你自己配一个Babel

我们会发现,实例方法includes被垫平,提案APIMath.signbit)也垫平;但它们都以局部变量的方式注入。

所以,如果我们不想以全局的方式污染的方式垫平我们的ES6+ API,我们corejs就不能为false,并且优先使用@babel/runtime-corejs3这个包来垫平(设置为3

优化

上面的案例我们并没有设置targets,所以@babel/plugin-transform-runtime配置下,会把我们代码中用到的所有ES6+ API都垫平。我们来设置targets,看看能不能垫平我们的targets不支持的ES6+ API

我们在package.json里面添加,或者在配置文件的顶层添加是可以的(经过测试,在@babel/preset-env里面设置targets是不行的):

// package.json
"browserslist": [
    "chrome 80"
]

// 或者在顶层添加
module.exports = {
    targets: {chrome: 80}, // 在这里添加
    presets: ['@babel/preset-env'],
    plugins: [
        [
            "@babel/plugin-transform-runtime",
            {
                ...
            }
        ]
    ]
}

输出: Babel配置不要再“复制粘贴”了,带你自己配一个Babel

我们会发现,Promiseincludes都没有被垫平,因为chrome 80已经支持了

总结

  • @babel/preset-env是以全局方式垫平,@babel/plugin-transform-runtime是以局部变量方式垫平,两者我们应该选择其一,不要又用@babel/preset-env配置方式,又用@babel/plugin-transform-runtime配置方式,这样肯定会出现一些奇奇怪怪的问题。
  • 因为使用@babel/plugin-transform-runtime垫平是以局部变量的方式来垫平,所以@babel/plugin-transform-runtime这种配置方式更适合来做 的开发。它可以很好的帮我们的库与使用者的项目解耦。

参考文章:babel-plugin-transform-runtime

其他

  • 案例 preset-env-useBuiltIns-config 里面有个stage-1阶段的提案API—— Array.prototype.lastItem(取数组最后一项)。本来是想用Array.prototype.uniqueBy这个当前处在stage-1API来给大家做例子的,结果发现编译以后在IE 11有问题。所以给Babel提了个 issues :

    Babel配置不要再“复制粘贴”了,带你自己配一个Babel

    目前应该是修复了,在等合并。复现问题的代码包在 babel-use-proposals-problem,感兴趣的朋友也可以Fork自己瞅瞅。说这个事主要也是想表达,学习还是不能停止(不是卷啊,还是要有学习的习惯)。

  • 在讲@babel/plugin-transform-runtime如何配置时,我们说到开启{version: 3, proposals: true}时,可以以局部变量的方式对提案阶段的ES6+ API垫平。

    但是经过测试发现,有些提案阶段的API,用这个方法似乎是不能按预期实现的,所以又提了个 issues ,有个Babel之一的大佬说可以用babel-plugin-polyfill-corejs3来实现:

    Babel配置不要再“复制粘贴”了,带你自己配一个Babel

    关于babel-plugin-polyfill-corejs3这块,其实Babel还有一种垫平配置方式,是使用 babel-polyfills 这个包来实现的,这块后期再看看要不要出文章聊聊。

最后

通过以上的了解,我们最后来总结一下这个配置。

  • 如果我们开发的项目是应用程序,或者大型的项目,那我们可以这么配置:
    // Babel配置
    const presets = [
        [
            '@babel/preset-env',
            {
                modules: false,
    
                // 或者只想用垫平我们代码中用到的 --> useBuiltIns: 'usage',
                useBuiltIns: 'entry',
                corejs: {
                    version: '3.27.2',
                    proposals: true
                }
            }
        ]
    ];
    const plugins = [
        '@babel/plugin-transform-runtime'
    ];
    module.exports = {plugins, presets};
    
    // package.json
    {
        ...,
        // 设置目标环境
        "browserslist": [
            "ie 11"
        ]
    }
    
    // 入口文件
    // ---- useBuiltIns: 'entry'时,需要引入以下----
    // 垫平全部ES6+稳定版API
    import 'core-js/stable'; 
    // ---- 或者 -----
    // 垫平所有ES6+ API,包括提案阶段
    import 'core-js';
    

  • 如果我们是想开发一个第三方库,我们可以这么配置:
    // Babel配置
    const presets = [
        [
            '@babel/preset-env',
            {
                modules: false
            }
        ]
    ];
    const plugins = [
        [
            '@babel/plugin-transform-runtime',
            {
                corejs: {
                    version: 3,
                    proposals: true
                }
            }
        ]
    ];
    module.exports = {plugins, presets};
    
    // package.json
    {
        ...,
        // 设置目标环境
        "browserslist": [
            "ie 11"
        ]
    }
    
    // 入口文件
    const Method = {
        wait(delay) {
            return new Promise(resolve => setTimeout(() => resolve(), delay);
        }
    }
    ...
    

文章涉及到的例子,已经上传 Github,觉得有帮助的话,欢迎Star或者Fork学习。

如果读完这篇文章的你,觉得真的有帮助到,欢迎点赞收藏;如果有异同点,欢迎在评论区讨论