阅读 1038

Babel polyfill 常见配置对比

前言

在解决了使用core-js@2的旧项目中,项目打包后使用 ES 新 api 报错的问题之后。对比了解 @babel/preset-env 、core-js@3 、@babel/runtime 三者的使用场景和区别,总结出如下内容。内容中如有不当之处,还望不吝指正,理性交流。

Babel 是什么

  • Babel 是一个 JS 编译器,通过 Babel 我们可以把按最新标准编写的 JS 代码向下编译成兼容浏览器或其他环境的通用版本。
  • 虽然 Babel 开箱即用,但是如果不做任何配置,输入原始代码,则会输出同样的原始代码。如果想要做一些实际工作,需要添加插件(plugins)。一个个手动引入需要的插件过于繁琐,所以通常会使用官方提供的预设(presets),预设是插件的集合。
  • 官方提供的预设:@babel/preset-env

插件和预设的执行顺序

  • 插件先执行,预设后执行
  • 插件集从前往后执行
  • 预设集从后往前执行

Babel 转译代码,会将代码分成两部分

  • syntax(语法):箭头函数、let、const、展开运算符等
  • api:Promise、includes、map等
  • Babel 默认不转换 syntax 和 api ,使用 @babel/preset-env 或 babel-runtime 后转换 syntax,但不会转换 api 。需要使用垫片(polyfill)转换 api,polyfill 可以让新的内置函数、实例方法等在低版本环境中也可以使用。

官方给出了两种 polyfill 方案

  • babel-polyfill:会污染全局适合在业务项目中使用。(Babel7.4.0版本开始,babel/polyfill 已经被废弃,推荐直接使用core-js)
  • babel-runtime:不污染全局适合在组件或类库项目中使用。

创建一个项目目录,来比较几种常见的 polyfill 配置

# 创建demo文件夹,进入并新建src文件夹和里面的index.js文件
mkdir demo && cd $_ && mkdir src && cd $_ && touch index.js

# 以默认配置初始化package.json
npm init -y            

# 安装 Babel核心库和命令行支持
npm install --save-dev @babel/core @babel/cli
复制代码

src/index.js的待编译代码

1.png

1.不做任何配置

配置 package.json 的 NPM Scripts 后,运行npm run build

  "scripts": {
    "build": "babel src --out-dir lib"
  },
复制代码

NPM 版本5.2以上,也可以直接运行编译命令:npx babel src --out-dir lib

会发现输出的 lib/index.js 文件,与编译之前没有区别,输出了原始代码。

在根目录添加文件:

  • babel.config.json(Babel配置文件)
  • .browserslistrc(声明了一段浏览器集合,根据这段集合描述,针对性的输出兼容性代码)

看看使用了配置和浏览器策略之后,会有哪些改变。

2.@babel/preset-env

  • 只转换syntax(class,typeof,箭头函数),不转换api(map,includes)
  • syntax的转换策略会根据浏览器策略(.browserslistrc文件的配置)改变
  • 安装:npm i @babel/preset-env -D

在 babel.config.json 文件中添加如下代码

{
    "presets": [
        [
            "@babel/preset-env"
        ]
    ]
}
复制代码

设置兼容较低版本的浏览器

// .browserslistrc
>0.25%
复制代码

编译之后的代码如下 2.png 设置兼容较高版本的浏览器

//.browserslistrc
Chrome > 75
复制代码

3.png

3.@babel/preset-env + core-js@3

  • 转换 syntax 和 api。
  • syntax 和 api 的转换策略会根据浏览器策略改变。
  • polyfill 从core-js@3 引入。
  • 安装:npm i core-js@3 -S
  • 使用 Webpack 打包后的文件体积约 23kb。
{
    "presets": [
        [
            "@babel/preset-env",
            {
                "corejs": 3,
                "useBuiltIns": "usage"
            }
        ]
    ]
}
复制代码

简单介绍一下配置项 useBuiltIns :默认为 false,可以使用的值有 usage 和 entry

  • usage:不需要手动 import '@babel/polyfill',会根据 browserlist + 业务代码使用到的新 API 按需自动加上 polyfill
  • entry:需要手动 import '@babel/polyfill',根据 browserlist 中浏览器版本的支持,将 polyfill 拆分引入浏览器不支持的 polyfill。这样会导致实际用不到的 polyfill 也会被打包到输出文件,导致文件比较大。
  • false:不启用 polyfill,如果 import '@babel/polyfill', 会无视 browserlist 将所有的 polyfill 加载进来。
  • 新版本的 Babel,会提示直接引入 core-js 或者 regenerator-runtime/runtime 来代替@babel/polyfill。

目标浏览器:>0.25%

4.png 目标浏览器:Chrome > 75

5.png

4.@babel/preset-env + @babel/runtime-corejs3 + @babel/plugin-transform-runtime

  • api转换会根据策略改变。
  • syntax转换会根据策略改变。
  • 安装:npm i @babel/plugin-transform-runtime -D @babel/runtime-corejs3 -S
  • 使用 Webpack 打包后的文件体积约 26kb。

@babel/runtime 和 @babel/plugin-transform-runtime 的关系:

  • plugin-transform-runtime 用于编译时转译代码,真正的polyfill在代码运行时从babel/runtime里引入,所以plugin-transform-runtime 需要安装在开发环境,而babel/runtime安装在生产环境

@babel/runtime 和 @babel/runtime-corejs3:

  • @babel/runtime包含:helpers、regenerator-runtime。只能处理语法。
  • @babel/runtime-corejs3包含:helpers、regenerator-runtime、core-js@3。引入core-js@3处理api。

根据 corejs 的选项,来选择对应的包即可:

6.png Babel 在每个需要转换的代码前面都会插入一些 helpers 代码,这可能会导致多个文件都会有重复的 helpers 代码。@babel/plugin-transform-runtime 的 helpers: true 选项就可以把这些代码抽离出来。

{
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": 3,
                "helpers": true,
                "regenerator": false
            }
        ]yu
    ],
    "presets": [
        [
            "@babel/preset-env"
        ]
    ]
}
复制代码

目标浏览器:>0.25%

7.png

目标浏览器:Chrome > 75

5.png

5.组合使用@babel/preset-env + core-js@3 + @babel/plugin-transform-runtime + @babel/runtime-corejs3

  • core-js 设置转译api, runtime 设置 false 不转译api。
  • runtime 转换了语法,没有转 api,core-js 转了api。
  • runtime 提取 helper 代码,减少重复代码,core-js 使用最新版本按需引入。
  • 使用 Webpack 打包后的文件体积约 12kb,体积最小
{
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": false,
                "helpers": true,
                "regenerator": false
            }
        ]
    ],
    "presets": [
        [
            "@babel/preset-env",
            {
                "corejs": 3,
                "useBuiltIns": "usage"
            }
        ]
    ]
}
复制代码

目标浏览器:>0.25%

8.png

目标浏览器:Chrome > 75

5.png

6.组合使用@babel/preset-env + core-js@3 + @babel/plugin-transform-runtime + @babel/runtime-corejs3,core-js和runtime都设置转译api

  • runtime转了语法和api,两者不会重复转译,输出结果与配置4一致。
{
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": 3,
                "helpers": true,
                "regenerator": false
            }
        ]
    ],
    "presets": [
        [
            "@babel/preset-env",
            {
                "corejs": 3,
                "useBuiltIns": "usage"
            }
        ]
    ]
}
复制代码

总结:

  • 个人测试的编译后体积最小的代码是配置5:使用 core-js 的 useBuiltIns 设置按需引入 polyfill 结合 @babel/plugin-transform-runtime 提取重复代码的 helper 代码。
  • 官方建议的使用方式是:根据使用场景,useBuiltIns 的 polyfill 全局范围添加,@babel/plugin-transform-runtime 的 polyfill 非全局范围添加,采用一种配置即可。
文章分类
前端
文章标签