# Babel 最佳实践

提到前端语法降级与垫片, 不得不做一番最新的 Babel7 实践总结

# 工具链分类

在开发中解决语法兼容问题, 主要需要用到两方面的工具,分别包括:

  • 编译时工具
  • 运行时基础库

# 编译时工具

Babel 编译时工具的作用是在代码编译阶段进行语法降级及添加 polyfill 代码的引用语句

主要代表工具有@babel/preset-env@babel/plugin-transform-runtime

该类工具仅编译阶段使用, 我们只需安装到package.json中的devDependencies

# 运行时基础库

Babel 的运行时基础库,主要包括core-jsregenerator-runtime两个基础库

对于该类基础库, Babel 官方做了一些上层封装, 主要包括以下几个库:

  • @babel/polyfill
  • @babel/runtime(特殊: 不包含 core-js 的 Polyfill)
  • @babel/runtime-corejs2
  • @babel/runtime-corejs3

对于这类库, 其实都是core-jsregenerator-runtime不同版本的封装罢了

这类库是项目运行时必须要使用到的,因此一定要安装到package.json中的dependencies

# 目标环境配置

Browserslist 是一个帮助我们设置目标浏览器的工具,不光是 Babel 用到,其他的编译工具如postcss-preset-envautoprefix中都有所应用。

对于 Browserslist 的配置内容,你既可以放到 Babel 这种特定工具当中,也可以在package.json中通过browserslist声明

// package.json
{
  "browserslist": "ie >= 11"
}
// .browserslistrc
ie >= 11
// 现代浏览器
last 2 versions and since 2018 and > 0.5%
// 兼容低版本 PC 浏览器
IE >= 11, > 0.5%, not dead
// 兼容低版本移动端浏览器
iOS >= 9, Android >= 4.4, last 2 versions, > 0.2%, not dead
// 通用配置
> 1%, last 2 versions, not dead

# Babel 配置

关于 targets 的配置

@babel/preset-envtargets设置有以下几种情况:

  • 如果未设置targets,那么就使用browserslist的配置
  • 如果对@babel/preset-envtargets参数项进行了设置,那么就不再使用browserslist的配置, 而是使用targets的配置
  • 如果targets不配置,browserslist也没有配置,那么@babel/preset-env就对所有 ES2015-ES2020 代码转换为 ES5 兼容

正常情况下,我们推荐使用browserslist的配置而很少单独配置@babel/preset-envtargets

# 应用级配置

如果使用新特性,往往是通过基础库(如 core-js)往全局环境添加 Polyfill,如果是开发应用没有任何问题,如果是开发第三方工具库,则很可能会对全局空间造成污染

  • 安装依赖
pnpm i @babel/runtime-corejs3 -S
pnpm i @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
  • 配置 babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        corejs: 3,
        useBuiltIns: 'usage',
        modules: false
      }
    ]
  ],
  plugins: [
    // 添加 transform-runtime 插件
    [
      '@babel/plugin-transform-runtime',
      {
        corejs: false,
        regenerator: false
      }
    ]
  ]
}

# 工具库配置

transform-runtime 一方面能够让我们在代码中使用非全局版本的 Polyfill,这样就避免全局空间的污染,这也得益于 core-js 的 pure 版本产物特性;另一方面对于 asyncToGeneator 这类的工具函数,它也将其转换成了一段引入语句,不再将完整的实现放到文件中,节省了编译后文件的体积

  • 安装依赖
pnpm i @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
pnpm i @babel/runtime-corejs3 -S
  • 配置 babel.config.js
module.exports = {
  plugins: [
    // 添加 transform-runtime 插件
    ['@babel/plugin-transform-runtime', { corejs: 3 }]
  ],
  presets: [
    [
      '@babel/preset-env',
      {
        // 关闭 @babel/preset-env 默认的 Polyfill 注入
        useBuiltIns: false,
        modules: false
      }
    ]
  ]
}

# 结合构建工具

# Vite 配置

注意

Vite 中不需要单独配置 babel.config.js

  • 安装依赖
# 必须安装 Terser,因为 plugin-legacy 使用 Terser 进行缩小。
pnpm i @vitejs/plugin-legacy terser -D
  • vite.config.ts 配置
// vite.config.ts
import legacy from '@vitejs/plugin-legacy'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    // 省略其它插件
    legacy({
      // 设置目标浏览器,browserslist 配置语法, 如果此处缺省则读取.browserslistrc
      targets: ['ie >= 11']
    })
  ]
})

# Rollup 配置

注意

Rollup 中需要单独配置 babel.config.js

  • 安装依赖
pnpm i rollup-plugin-babel -D
# rollup-plugin-babel插件并不包含babel包,需要安装必要的babel包依赖
pnpm i @babel/runtime-corejs3 -S
pnpm i @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
  • rollup.config.js 配置
// rollup.config.js
import babel from 'rollup-plugin-babel'

export default {
  /* 省略其他配置... */
  plugins: [
    babel({
      exclude: 'node_modules/**' // 只编译我们的源代码,忽略第三方代码
    })
  ]
}

# Webpack 配置

注意

Webpack 中需要单独配置 babel.config.js

  • 安装依赖
pnpm i @babel/runtime-corejs3 -S
pnpm i @babel/core @babel/preset-env @babel/plugin-transform-runtime babel-loader -D
  • webpack.config.js 配置
module.exports = {
  /* 省略其他配置... */
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ['babel-loader']
      }
    ]
  }
}