Babel 浅入浅出

157 阅读4分钟

这边文章主要想记录一下 在一个项目中关于babel 的基本配置 包括 preset-env 配置 useBuiltIns 的用法 以及 @babel/plugin-transform-runtime 插件的使用 弄明白 为什么要配置这些?弄明白每一个配置的含义

1 准备工作

首先 创建项目 -> npm 初始化 -> 安装依赖

mkdir babel-practice

cd babel-practice

npm init -y

npm install  @babel/core @babel/cli @babel/preset-env -D

2 创建目录src 以及 package.json scripts 配置

image.png

"scripts": {
  "start": "babel src --out-dir lib"
}

3 尝试转译 一个文件

// src/index.js
const arr = [1,2,3,4,5]
const arr1 = arr.filter((item)=>{
  return item > 2
})
arr1.includes(4)


//转译结果
// lib/index.js
const arr = [1, 2, 3, 4, 5];
const arr1 = arr.filter(item => {
  return item > 2;
});
arr1.includes(4);

如果没有配置preset-env 则会原封不动的输出 所以需要配置 preset

4 preset-env 配置

创建 babel.config.js

module.exports = {
  presets:[
    "@babel/preset-env"
  ]
}

再次转译

// src/index.js
const arr = [1,2,3,4,5]
const arr1 = arr.filter((item)=>{
  return item > 2
})
arr1.includes(4)

//转译结果
// lib/index.js
var arr = [1, 2, 3, 4, 5];
var arr1 = arr.filter(function (item) {
  return item > 2;
});
arr1.includes(4);

可以看到 const 以及 箭头函数 被转译了 而 arr.includes 没有被转译 原因: babel 在转译的时候,会将源代码分成 syntax 和 api 两部分来处理

Babel 默认只转换新的 JavaScript语法,比如const let 箭头函数,而不转换新的API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法 比如:includes Array.from 等都不会转译。 如果想使用这些新的对象和方法,必须使用 polyfill ( 或者 @babel/runtime ) 来处理

5 配置 useBuiltIns

我们先使用 'entry' + import "@babel/polyfill" 方式:

// babel.config.js
  module.exports = {
    presets:[
      [
        "@babel/preset-env",
        {
          "useBuiltIns": "entry"
        }
      ]
    ]
  }
// index.js
import "@babel/polyfill"

const arr = [1,2,3,4,5]
const arr1 = arr.filter((item)=>{
  return item > 2
})
arr1.includes(4)

转译结果:

image.png

如图所示:

这种方式引入了全量的 polyfill(包括 core-js/modules/xxxx 和 regenerator-runtime/runtime.js )

但是我们在这里只需要引入支持includes 和 filter 的polyfill 即可

所以采用 按需引入的方式 "useBuiltIns": "usage"

// babel.config.js
  module.exports = {
    presets:[
      [
        "@babel/preset-env",
        {
          "useBuiltIns": "usage"
        }
      ]
    ]
  }

结果:

image.png 貌似比较好的解决了includes的转译问题了 但是 这里有个问题

arr.includes 本身并没有发生变化 通过 require polyfill 的方式只是在Array.prototype上添加了 includes 方法 (Array.prototype.includes )

上面这种方式污染了 Array 以及 Array.prototype 这种污染全局属性的方式一般在编程理念中是不推荐的

再有像 class 以及 Async 这些特殊语法在被转译时 babel会在当前文件中定义一些辅助方法来实现转译 这些方法被称为 helpers 拿 class 尝试一下

// src/class.js

class Abc{
  constructor(name) {
    this.name = name
  }
}
const abc = new Abc('abc')

转译结果:

image.png

图中可以看到_createClass _classCallCheck方法在当前文件被定义 这里也有个问题

如果我在多个文件中都定义了 class xxx (这是很常见的) 那岂不是所有的文件中 都有一份这样的定义 这样就太糟糕了

image.png

该如何解决?

6 @babel/plugin-transform-runtime 插件

@babel/plugin-transform-runtime这个插件的作用就是解决上面提到的两个问题

它的作用是 通过模块化的方式 从runtime中引入对应的转译方法 在不污染全局作用域的同时也不会在每个文件中重复定义

如果使用了 @babel/plugin-transform-runtime 则不再需要配置 "useBuiltIns": "usage"

先安装插件:

npm install @babel/plugin-transform-runtime -D

在 babel.config.js 中修改配置

```js
module.exports = {
  presets:[
    [
      "@babel/preset-env"
    ]
  ],
  plugins:[
    "@babel/plugin-transform-runtime"
  ]
}

src/class.js 中 转译的结果如下:

image.png

但是 src/index.js 的转译结果貌似有些异常 filter 和 includes 未被转译

image.png

导致这个问题的原因是因为我们再使用 @babel/plugin-transform-runtime 时没有配置 corejs corejs 配置可选项 为 false / 2 / 3 分别对应的不同的运行时插件来处理

image.png

corejs的默认配置是false

在这里有必要先了解一下这几个包的关系

  • @babel/polyfill
  • @babel/runtime
  • @babel/runtime-corejs(2|3)
  • @babel/plugin-transform-runtime
  1. @babel/polyfill仅仅是引用core-js、regenerator-runtime这两个npm包。
  2. @babel/runtime包含两个文件夹:helpers(定义了一些处理新的语法关键字的帮助函数)、regenerator(引用regenerator-runtime)
  3. @babel/runtime-corejs2 包含三个文件夹:core-js、helpers、regenerator @babel/runtime-corejs2 ≈ @babel/runtime + babel-polyfill:
  4. 如果没有 @babel/plugin-transform-runtime 则 @babel/runtime相当于是手动挡 需要手动引入 对应的 helpers @babel/plugin-transform-runtime 能帮助你自动引入
@babel/runtime / @babel/runtime-corejs(2|3) 对比

@babel/runtime只能处理语法关键字

@babel/runtime-corejs2 支持全局变量(例如 Promise)和静态属性(例如 Array.from)但不支持 实例属性

@babel/runtime-corejs3 支持实例属性(例如 [].includes)。

了解完这些后继续回到上面的问题 由于@babel/plugin-transform-runtime 的 corejs 没有配置 默认为false 对应的runtime为@babel/runtime 他只能处理语法关键字 没法对 includes 等实例属性转译 因此此处需要配置 corejs: 3

module.exports = {
  presets:[
    [
      "@babel/preset-env"
    ]
  ],
  plugins:[
    [
      "@babel/plugin-transform-runtime",
      {
        corejs: 3
      }
    ]
  ]
}

配置corejs:3 后的转译结果为:

image.png

这一步终于完成了最基本的配置了

后续有时间再继续探讨