Babel 学习笔记

Babel 学习笔记

参考文档

babel是什么

Babelpolyfill的关系:Babel 默认只转换新的 JavaScript 语法,而不转换新的 API。所以当使用 Array.prototype.findObject.assign等静态方法和实例方法时,需要引入polyfill

Babel 是一个编译器(输入源码 => 输出编译后的代码)。可以让你提前使用新的语法,而不用管浏览器是否已经支持。

实现的方式为:1、转换源代码为目标浏览器支持的语法;2、添加新功能的polyfill;

编译过程分为三个阶段:解析、转换和打印输出。

babel工作模式

输入文本 =》输出文本;

支持plugin模式,本质就是对源文本内容转换后输出新的源文本给下一级。

preset,用于预定义一组plugin集合,这样不用再手动一个个引入plugin。

@babel/preset-env,babel官方提供的一个preset。

Plugin插件

转换插件语法插件

注意:转换插件会自动启用语法插件。因此,如果你已经使用了相应的转换插件,则不需要指定语法插件。

转换插件

官网

这些插件用于转换你的代码。

转换插件会自动启用语法插件。因此,如果你已经使用了相应的转换插件,则不需要指定语法插件。

语法插件

这些插件只允许 Babel 解析(parse) 特定类型的语法(而不是转换)。

parserOpts ??

插件名称

  1. npm可以直接指定名称,如果插件名称的前缀为 babel-plugin-,你还可以使用它的短名称。 babel-plugin-myPlugin -> myPlugin。带scope的插件也一样。
  2. 还可以使用相对/绝对路径。
1
2
3
4
5
6
7
8
9
{
"plugins": [
"myPlugin",
"babel-plugin-myPlugin", // 两个插件实际是同一个
"./node_modules/asdf/plugin",
"@org/babel-plugin-name",
"@org/name" // 两个插件实际是同一个
]
}

插件顺序

将根据转换插件或 preset 的排列顺序依次执行。

  • 插件在 Presets 前运行。
  • 插件顺序从前往后排列。
  • Preset 顺序是颠倒的(从后往前)。

插件参数

插件和 preset 都可以接受参数,参数由插件名和参数对象组成一个数组。

如果不指定参数,下面这几种形式都是一样的:

1
{ "plugins": ["pluginA", ["pluginA"], ["pluginA", {}]] }

要指定参数,则传递一个以参数名作为键(key)的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{ 
"plugins": [
[
"transform-async-to-module-method",
{
"module": "bluebird",
"other": {
"a": "aaa",
"b": "bbb"
}
}
]
]
}

preset 的设置参数的工作原理完全相同。

插件开发

参考手册

本质上就是一个函数。

预设(Presets)

官方 Preset

我们已经针对常用环境编写了一些 preset:

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

Stage-X (实验性质的 Presets)

这些提案可能会有变化,因此,特别是处于 stage-3 之前的任何提案,请务必谨慎使用

TC39 将提案分为以下几个阶段:

  • Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
  • Stage 1 - 建议(Proposal):这是值得跟进的。
  • Stage 2 - 草案(Draft):初始规范。
  • Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
  • Stage 4 - 完成(Finished):将添加到下一个年度版本发布中

创建 Preset

如需创建一个自己的 preset,只需导出一份配置即可。preset 可以包含其他的 preset,以及带有参数的插件。

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = function() {
return {
presets: [
require("@babel/preset-env")
],
plugins: [
"pluginA",
require("@babel/plugin-proposal-object-rest-spread"),
[require("@babel/plugin-proposal-class-properties"), { loose: true }],
]
};
}

preset-env 与 polyfill

官方文档
知乎 - Babel7 中 @babel/preset-env 的使用

babel使用 preset-env能将最新的语法转换为ecmascript5的写法,当我们需要使用新增的全局函数(比如promise, Array.from)和实例方法(比如 Array.prototype.includes )时就需要引入 polyfill, 一般用法在 index.js文件的最上层引入,或是在打包文件的entry 入口处引入,这样做的缺点是会全局引入整个polyfill包,比如promise会全局引入,污染全局环境。

引入方式:

1
2
3
4
import "@babel/polyfill";
// babel7开始推荐使用下面的方式引入,使用require也可以。
import "core-js/stable";
import "regenerator-runtime/runtime";

与 useBuiltIns 结合使用

结合useBuiltIns选项可以定义@babel/polyfill的引入方式。

  • entry:需要在项目入口文件最顶部引入import '@babel/polyfilll',会结合targets转换为一系列引入语句,去掉目标浏览器已支持的polyfilll模块,不管代码里有没有用到,只要目标浏览器不支持都会引入对应的polyfilll模块。
  • usage:不需要手动在代码里写import '@babel/polyfilll',打包时会自动根据实际代码的使用情况,结合 targets 引入代码里实际用到的部分polyfilll模块,但任然需要被install
  • false:需要手动引入,babelimport '@babel/polyfilll'不作任何处理,也不会自动引入 polyfilll 模块。官方建议在webpack配置中作为入口(entry)引入,而不是在入口 js文件中使用import/require语句
1
2
3
4
// useBuiltIns选项为false 或 干脆就没用preset-env 的情况下,建议使用该方式引入polyfill
module.exports = {
entry: ["@babel/polyfill", "./app/js"],
};

@babel/plugin-transform-runtime

babel的polyfill和runtime的区别

babel-polyfill 使用场景

Babel 默认只转换新的 JavaScript 语法,而不转换新的 API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。如果想使用这些新的对象和方法,必须使用 babel-polyfill,为当前环境提供一个垫片。

babel-runtime 使用场景

Babel 转译后的代码要实现源代码同样的功能需要借助一些帮助函数,而这些帮助函数可能会重复出现在一些模块里,导致编译后的代码体积变大。Babel 为了解决这个问题,提供了单独的包 babel-runtime 供编译模块复用工具函数