本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
本文首发于个人博客。
1.前言
2021-10-18更新:在github上添加了一个示例仓库,配合食用更佳。
Babel是什么?
Babel是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
Babel 的中文解释是巴别塔。在《圣经》当中,有这样的故事。当时地上的人们都说同一种语言,当人们离开东方之后,他们来到了示拿之地。在那里,人们想方设法烧砖好让他们能够造出一座城和一座高耸入云的塔来传播自己的名声,以免他们分散到世界各地。上帝来到人间后看到了这座城和这座塔,说一群只说一种语言的人以后便没有他们做不成的事了;于是上帝将他们的语言打乱,这样他们就不能听懂对方说什么了,还把他们分散到了世界各地,这座城市也停止了修建。这座城市就被称为巴别城,这座塔即是巴别塔。
可以将 Babel 看做语言的统一。Babel 的作者将其取名为 Babel,是有相当一部分野心的。他是想将目前不同的 JS 语言规范,统一成一种,实现不同浏览器下的兼容。
截止到目前,babel 也有过很多次更新了。该文主要将以 babel@7.0 版本来理解 babel 的使用。
Babel做什么?
常用的功能有3种:
- 语法转换。譬如将
es6的箭头函数转换为低版本浏览器兼容的普通函数。 - 通过
Polyfill方式在目标环境中添加缺失的特性 (通过@babel/polyfill模块)。 - 源码转换。譬如转换
typescript、jsx等。
了解步骤
下面我们会从 CLI 、 plugins 、presets 以及 polyfill 等一步步来了解babel的用法。
准备工作
- 新建目录。
- 执行
yarn init -y。 - 创建
src目录。 - 在
src下创建入口文件main.js。
main.js 文件的内容如下:
// 1.arrow function
const arrowFun = () => {
console.log('arrow-function', this)
}
// 2.class
class Person {
constructor() {
this.name = name
}
say() {
alert('hello')
}
}
// 3.promise es6新增
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() * 10 >= 5) {
resolve('大于5')
} else {
reject('小于5')
}
}, 2000)
})
// 4.async await es7
async function fn() {
try {
const result = await promise
console.log(result)
} catch(err) {
console.warn('error', err)
}
console.log('--- after promise ---')
}
fn()
// 5.includes
const flag = [1, 2, 3].includes(1)
console.log('includes', flag)
之所以创建这些例子,是有原因的。当前目录结果大致如下:
.
├── package.json
├── src
│ └── main.js
└── yarn.lock
下文的讲述都是基于该项目。
2.CLI
1.脚手架@babel/cli
babel 作为一门转换工具。它自带了一套自己的脚手架工具。
新建一个项目,执行:
yarn add @babel/cli -D
从 babel@7.0 版本开始,babel 会将它所有的官方包都放在 @babel 这个命名空间下。其实这并不是 babel 的特例。而是 npm 对于它自身包管理的一种优化。 babel 只是顺道遵守了而已。
这样的话,我们就可以使用 babel 命令行。由于环境变量的原因,所以我们配置下 package.json (你也可以利用 npx 取代这种方式)。利用 npm 来执行 babel:
{
"scripts": {
"compiler": "babel './src/main.js' --out-dir dist"
}
}
当执行 yarn compiler 时,按照道理来说,babel 就会将 src 目录下的 main.js,转译到 dist 目录中。
但现在执行 yarn compiler 后,会发现 dist/main.js 文件中的代码并没有任何转换迹象。
2.核心库@babel/core
babel的核心功能包含在 @babel/core 模块中。安装:
yarn add @babel/core -D
这时执行 yarn compiler, 代码仍然不会转换。
可以将 @babel/core 理解成一个核心转换模块函数,它的执行依赖于 options 配置。如果没有 options ,那么这个函数什么也不会做。
function core (code, options = []) {
options.forEach(item => {
code += item
})
return code
}
而这些 options 在 babel 中对应的就是 plugins。
3.插件plugins
安装下转换箭头函数的插件:
yarn add @babel/plugin-transform-arrow-functions -D
在 package.json 中的 scripts 添加命令:
{
"scripts": {
"compiler:plugin": "babel './src/main.js' --out-dir dist --plugins=@babel/plugin-transform-arrow-functions"
}
}
再次执行 yarn compiler:plugin,会发现 main.js 中的箭头已被转换:
var _this = this;
// 1.arrow function
const arrowFun = function () {
console.log('arrow-function', _this);
};
4.预设presets
presets 其实就是一组 plugins 的集合。官方提供的预设有四组:
@babel/preset-env@babel/preset-flow@babel/preset-react@babel/preset-typescript
其中 @babel/preset-env 代表的是对于环境 environment 的预设。现在我们使用 --presets 来代替上面使用的 --plugins。
{
"scripts": {
"compiler:preset": "babel './src/main.js' --out-dir dist --presets=@babel/preset-env"
}
}
执行 yarn compiler:preset。会发现代码已经被转换成ES5代码(除了需要@babel/polyfill的语法。如promise等。)
另外直接执行转译过后的代码,会有报错 ReferenceError: regeneratorRuntime is not defined。这是因为 async await 的实际运行也需要 @babel/polyfill。
具体代码可见下方。
在未指定浏览器目标的情况下,@babel/preset-env 会将所有 ES2015-ES2020 代码转换为与ES5兼容。
但是不建议以这种方式使用 @babel/preset-env ,因为它没有利用针对特定环境/版本的功能。
"use strict";
var _this = void 0;
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
// 1.arrow function
var arrowFun = function arrowFun() {
console.log('arrow-function', _this);
};
// 2.class
var Person = /*#__PURE__*/function () {
function Person() {
_classCallCheck(this, Person);
this.name = name;
}
_createClass(Person, [{
key: "say",
value: function say() {
alert('hello');
}
}]);
return Person;
}();
// 3.promise es6新增
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
if (Math.random() * 10 >= 5) {
resolve('大于5');
} else {
reject('小于5');
}
}, 2000);
});
// 4.async await es7
function fn() {
return _fn.apply(this, arguments);
}
function _fn() {
_fn = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
var result;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
_context.next = 3;
return promise;
case 3:
result = _context.sent;
console.log(result);
_context.next = 10;
break;
case 7:
_context.prev = 7;
_context.t0 = _context["catch"](0);
console.warn('error', _context.t0);
case 10:
console.log('--- after promise ---');
case 11:
case "end":
return _context.stop();
}
}
}, _callee, null, [[0, 7]]);
}));
return _fn.apply(this, arguments);
}
fn();
// 5.includes
var flag = [1, 2, 3].includes(1);
console.log('includes', flag);
5.总结
这一章,主要来了解快速使用 babel 需要安装什么。
@babel/cli, 官方脚手架。@babel/core, 核心转换模块。@babel/plugins或者@babel/presets, 具体的转换规则。@babel/polyfill, 低版本浏览器需要腻子。不过babel已经有新的使用方法来弥补@babel/polyfill存在的缺点。
后面我们会循序渐进的来深入了解。
3.config files
在前一章,使用的 yarn compiler:plugins 以及 yarn compiler:presets,都是直接在命令行中配置了 plugins 及 presets。
这种方式不够优雅,而且不够友好。所以在理解 plugins 与 presets 之前,先来了解下 babel 提供的专门的配置文件来替代这种方式。
该配置文件有两种类型:
- 项目范围的配置
babel.config.json文件。可使用扩展名有.json、.js、.cjs、.mjs。
- 相对文件配置
.babelrc.json文件。可使用扩展名有.json、.js、.cjs、.mjs。或者直接命名为.babelrc。package.json文件。 其中有key为babel的设置。
在项目根目录下设置这两种类型的文件。babel 在执行的时候会自动寻找。
1.项目范围的配置
babel@7.0 开始,具有了根目录的概念。默认为当前工作目录。(babel 命令执行的目录)。
项目范围的配置,默认的搜索行为是,在当前执行转译命令的目录中直接寻找babel.config.json,找到的话,正常编译,否则不会编译。
babel-config-demo/
.
├── package.json
├── node_modules
├── src
│ ├── main.js
│ └── package.json
├── babel.config.js
└── yarn.lock
假设在项目 babel-config-demo中的目录结构如上。
那么当在 babel-config-demo/ 目录下执行 babel 命令时,是可以正常转码的。而在 babel-config-demo/src 下无法转码。
另外也可以使用 configFile 选项来指明具体的 babel.config.json 路径。需要注意的是,该选项仅允许利用编程方式使用。即创建 js 文件,手动引入 @babel/core, 调用 api。
此外,babel.config.json 也能对 node_modules 和 symlinked packages 内的文件进行转码。而 .babelrc.json 则不能。
从上面的论述中,我们简单总结下 babel.config.json 类文件的特点:
- 必须存在于执行目录。执行命令的目录下,必须能找到
babel.config.json。 - 如果项目特殊,可以利用
configFile选项,显式的指定babel.config.json的位置。 babel.config.json也能对node_modules和symlinked packages内的文件进行转码。
2.相对文件的配置
相对文件的配置,默认搜索行为是,根据执行命令先定位到转译的目标文件,基于目标文件的位置,逐次向上排查.babelrc.json。此搜索过程中,有两点注意:
- 在此搜索过程中,一旦遇到
package.json时,此搜索就会停止。 - 如果找到了
.babelrc.json,则此.babelrc.json必须与执行命令在同一级目录。否则.babelrc.json会被忽略,文件不会转译。
示例一
babelrc-demo/
.
├── package.json
├── node_modules
├── src
│ ├── main.js
│ └── package.json
├── .babelrc
└── yarn.lock
在 babelrc-demo/ 下执行 yarn compiler 命令时,会先定位到 src/main.js 。然后顺此文件向上查找 .babelrc。但由于 src 文件夹内有 package.json,所以查找会立刻停止,直接在 src 下寻找 .babelrc。可想而知,并不会正常执行。
示例二
babelrc-demo/
.
├── package.json
├── node_modules
├── src
| └── .babelrc
│ ├── main.js
│ └── package.json
├── .babelrc
└── yarn.lock
该例相比上例,在 src 下多了 .babelrc。
同样,在 babelrc-demo/ 下执行 yarn compiler 命令时,会先定位到 src/main.js 。虽然此时在 src 下可以找到 .babelrc。但由于命令是在 babelrc-demo/ 下,而 .babelrc 是在 babelrc-demo/src/ 下,两者并不在同一目录,结果也是执行失败。
总结
在大部分情况下,使用项目范围的配置与相对文件的配置,差异并不大。
babel 之所以要将配置文件分为这两类,主要是为了方便开发者管理类似 @babel 这种 mono packages 项目。既能统一集中的管理通用的 babel 配置(项目范围的配置),又能根据各个 package 的实际情况做单独的配置(相对文件的配置)。当两种配置同时找到了的时候,相对文件的配置,将会与项目范围的配置进行合并,然后才应用到子package。
4.plugins
就像前文已经提过的,babel 的核心转换功能是在@babel/core。@babel/core 本身只是一个转换器,如果没有配置 plugins 声明转换规则的话,babel 什么都不会做。
插件分为两类:转换插件 与 语法插件。
转换插件
在 .babelrc 中配置:
{
"plugins": ["@babel/plugin-transfrom-arrow-fuctions"]
}
这里列举些 es6 常见的插件:
箭头函数:@babel/plugin-transform-arrow-functions
class: @babel/plugin-transform-classes
for of: @babel/plugin-transform-for-of
语法插件
在 .babelrc 中配置:
{
"parserOpts": {
"plugins": ["jsx", "flow"]
}
}
在使用某些转换插件的时候,会默认启用对应依赖的语法插件。
所以一般我们不用过于注重这类插件的种类和配置。
插件顺序
- 插件在
presets前执行。 - 插件顺序从前往后运行。
presets顺序是颠倒的(从后往前)。这个是为了由于babel版本的迭代原因,保证向下兼容。
插件参数
给插件设置参数的时候,可以将插件项写作数组形式。
{
"plugins": [
[
"@babel/plugin-transform-arrow-functions", {
"key": "value"
}
]
]
}
5.presets
预设 是一组插件的集合。这是为了方便开发者在实际应用中无需再手动配置各类插件。
官方预设
babel 官方已经提供了一些预设。
@babel/preset-env@babel/preset-flow@babel/preset-react@bbael/preset-typescript
预设的使用配置跟插件的大致相同。
唯一要注意的是,预设的执行顺序与插件是相反的。
@babel/preset-env
该预设可以称作环境预设。
提到环境,有非常重要的两点:代码转换 与 polyfill。恰好 @babel/preset-env 帮我们把这两件事都比较优雅的实现了。
又由于浏览器的版本众多,我们必须在使用预设的时候告诉它,我们的目标浏览器是什么及是什么版本。也就是需要设置 browserslist。
为了行文方便,本章节所有 preset-env 皆指代 @babel/preset-env。
browserslist
在 babel 配合 preset-env使用,共有三种配置方法:
.browserslistrc在项目根目录下添加.browserslistrc文件。要注意的是,该文件是全局配置。譬如如果项目有postcss,那么它也会读取该文件。
> 1%
last 2 versions
not ie <= 8
package.json中设置browserslist。该配置在项目范围中的作用及优先级同上项。 数组形式:
{
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
字符串形式:
{
"browserslist": "> 1%, last 2 versions, not ie <= 8"
}
- 给预设
@babel/preset-env设置target参数。优先级在这三者中最高。
{
"presets": [
[
"@babel/preset-env", {
"targets": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
]
]
}
options
下面是一些常见的设置属性。
targets
string | Array<string> | { [string]: string },defaults to {}。
用来设置目标浏览器。
modules
"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false,defaults to auto。
用来设置转译后的代码采用何种模块化方式。设置为 false时,将保留采用 ES Module。
在 webpack 项目中,强烈建议设置为 false,将 import 交由 webpack 处理。
因为 webpack 可以对 ES Module 做 tree shaking。
useBuiltIns
"usage"| "entry" | false。default to false。
用来配置 preset-env 如何处理 polyfill。
"usage"自动按需导入。"entry"入口文件处引入polyfill,preset-env会全量导入polyfill。false不再使用polyfill。
但 preset-env 并不内置 polyfill,它只是一系列插件的集合。所以我们在使用该配置属性前需要安装@babel/polyfill。
babel@7.4.0 已开始弃用 @babel/polyfill。推荐使用core-js。当使用 core-js 时,需要配合下个属性进行设置。
core-js
2, 3 or { version: 2 | 3, proposals: boolean }, defaults to 2
用来配置 core-js 的版本。
当设置其版本为 2 或 3 时,需要对应安装 core-js@2 或 core-js@3。
ignoreBrowserslistConfig
Boolean, defaults to false.
配置是否忽略 browserslist 文件及 package.json 中的 browserslist 键。
6.polyfill
首先要明确的一点是,什么是 polyfill? 为什么需要 polyfill?
定义
polyfill 意为腻子。它负责抹平不同环境下的API差异。
这里有一篇之前更新的文章来介绍它。
babel 本身只能转换已经存在的语法,譬如可以将箭头函数转换成普通函数、将class转换成构造函数。
但它不能转换新语法,譬如 Promise、includes、map 等,这些指的都是在全局或者Object 、Array 等的原型上新增的方法。
@babel/polyfill
该库本来是 babel 提供 polyfill 的独立库。
从 babel@7.4.0 开始,推荐直接引入这俩库来代替 @babel/polyfill 。
import 'core-js/stable'
import 'regenerator-runtime/runtime'
@babel/polyfill 的使用
原始使用
直接在入口文件中引入全量包:
import '@babel/polyfill'
结合 @babel/preset-env 使用
babel 不推荐直接在入口文件当中引入 @babel/polyfill。因为这种方式会引入全量包,导致一些不需要的 polyfill 也会加载进去,增大了包的体积。
因此,往往结合 preset-env 使用 polyfill。
- 当设置
useBuiltIns为false,或者不设置useBuiltIns选项。需要利用webpack的entry属性,将其设置为数组形式:
// webpack.config.js
module.exports = {
entry: ['@babel/polyfill', './src/main.js']
}
- 当设置
useBuiltIns为'entry',根据配置项corejs的值,具体配置也是不同的。'entry'意为入口,babel会在入口处寻找polyfill并导入全量包。
// 当 corejs:2 时,入口文件main.js引入
import '@babel/polyfill'
// 额外安装 yarn add core-js@2
// 当 corejs:3 时,入口文件main.js引入
import "core-js/stable"
import "regenerator-runtime/runtime"
// 额外安装 yarn add core-js@3
- 当设置
useBuiltIns为'usage',polyfill会自动被按需引入,只会加载用到的polyfill, 但要注意的是仍需安装@babel/polyfill包。只是不需要手动配置而已。
由于 babel 逐渐弃用 polyfill,所以在设置 useBuiltIns 时,有可能会遇见错误:
WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.
You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands:
npm install --save core-js@2 npm install --save core-js@3
yarn add core-js@2 yarn add core-js@3
显而易见,babel 推荐使用 core-js。所以解决报错方法是安装 core-js 并在 preset-env 声明其使用版本。
@babel/polyfill 的废弃
@babel/polyfill 被废弃的原因有两个:
- 每个转译的文件都可能会生成大量重复的
helper工具函数,代码冗余,包体积增大。 @babel-polyfill修改全局变量。不利于第三方公共库的使用。
我们来看下使用 preset-env 和 @babel/polyfill 转译后的代码。摘抄了下核心部分:
"use strict";
require("@babel/polyfill");
var _this = void 0;
// ①文件顶部存在大量的 helper 工具函数
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
// ②这里是 async 的转译代码。可以发现凭空多了 regeneratorRuntime 这个变量。
function _fn() {
_fn = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
var result;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
_context.next = 3;
return promise;
case 3:
result = _context.sent;
console.log(result);
_context.next = 10;
break;
case 7:
_context.prev = 7;
_context.t0 = _context["catch"](0);
console.warn('error', _context.t0);
case 10:
console.log('--- after promise ---');
case 11:
case "end":
return _context.stop();
}
}
}, _callee, null, [[0, 7]]);
}));
return _fn.apply(this, arguments);
}
从上述代码可以发现两部分问题:
-
_interopRequireDefault、asyncGeneratorStep、_asyncToGenerator、_classCallCheck、_defineProperties、_createClass是由preset-env生成的helper工具类函数。如果有多个babel转译的文件,这些文件中都会存在这些函数。 -
多了一个全局变量
regeneratorRuntime。@babel/polyfill的引入会暴露这个全局变量以保证代码的正常运行。否则运行代码,会报错regeneratorRuntime is not defined。
这两部分问题的解决是依赖于@babel/plugin-transfrom-runtime。它会把上面的 helper 工具函数以及 regeneratorRuntime 统一从 @babel/runtime 这个库中导入。
7.transform runtime
在上一章的结尾处,我们简单引出了 @babel/plugin-transform-runtime 。
它主要用来解决 @babel/polyfill 结合 preset-env 使用时出现的问题。
这一章我们详细介绍下它。
安装
@babel/plugin-transform-runtime 是 babel 的一个插件。它通常需要结合 @babel/runtime 使用。
yarn add @babel/plugin-transform-runtime -D
# 这个库可能不用手动安装 在安装上面插件时,babel可能会自动安装runtime这个库。另外由于是生产依赖,所以这里不加 '-D'
yarn add @babel/runtime
使用原因
@babel/plugin-transform-runtime 用来替代 @babel/polyfill。它的主要优势有两个:
- 在转译的单个代码文件中,就会存在很多的
helper工具函数,如果项目中多个文件转译,可想而知,会存在大量重复的helper工具函数,增大项目包体积。
@babel/plugin-transform-runtime 会将所有的 helper 工具函数统一从 @babel/runtime 库中引入。
@babel/polyfill会污染全局作用域,不利于在第三方库使用。
@babel/plugin-transform-runtime 会创建一个沙盒环境,保证代码环境不被污染。
使用方法
因为 @babel/plugin-transform-runtime 是插件,所以直接用插件形式配置即可。以 babelrc 文件为例:
{
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 3,
"helpers": true,
"regenerator": true,
"useESModules": false,
}
]
],
"presets": []
}
配置选项
1.corejs
2, 3 or { version: 2 | 3, proposals: boolean }, defaults to 2
用来声明 @babel/plugin-transform-runtime 转译代码后的 core-js 版本。
该选项相当于替代 polyfill。设置后无需再使用 polyfill。会有对应版本的 @babel/runtime-corejs。
core-js@2 仅支持全局变量(例如 Promise )和静态属性(例如 Array.from )。
core-js@3 还支持实例属性 (例如 [].includes )。
所以一般我们设置为 corejs: 3。
2.helpers
Boolean, defaults to true.
设置是否将 _asyncToGenerator、_classCallCheck、_createClass 等 helpers 转换成 模块化导入。
// polyfill下 是在文件头部声明一堆函数
// transform-runtime下设置 corejs: 3
import _asyncToGenerator from "@babel/runtime-corejs3/helpers/esm/asyncToGenerator";
import _setTimeout from "@babel/runtime-corejs3/core-js-stable/set-timeout";
import _createClass from "@babel/runtime-corejs3/helpers/esm/createClass";
3.regenerator
Boolean, defaults to true.
设置是否将 _regeneratorRuntime 转换成 模块化导入。
// polyfill下 是一个全局变量
// transform-runtime下设置 corejs: 3
import _regeneratorRuntime from "@babel/runtime-corejs3/regenerator"
@babel/runtime
当不设置 @babel/plugin-transform-runtime 的 corejs 选项时,默认会从 @babel/runtime 中导入 helpers 以及 regenerator。
注意:不设置 corejs 时,转译后的代码类同于设置 corejs: 2。不支持实例属性。
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
@babel/runtime-corejs2
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/createClass"));
@babel/runtime-corejs3
import _regeneratorRuntime from "@babel/runtime-corejs3/regenerator";
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _asyncToGenerator from "@babel/runtime-corejs3/helpers/esm/asyncToGenerator";
import _setTimeout from "@babel/runtime-corejs3/core-js-stable/set-timeout";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
import _classCallCheck from "@babel/runtime-corejs3/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime-corejs3/helpers/esm/createClass";
8.总结
在前面的介绍之后,我们来总结下 babel@7.0 版本及以上的配置方法。
浏览器兼容方面,通常有两种:@babel/polyfill 和 @babel/plugin-transform-runtime。
预设方面,通常采用 @babel/preset-env 即可。根据上面采用的 polyfill 和 transform-runtime 不同,preset-env 的配置有所差异。
下面给出对应配置:
polyfill
// babel.config.js
module.exports = {
presets: [
["@babel/preset-env", {
// 默认采用ES Module, 利于webpack进行tree shaking
modules: false,
// 可选值 usage entry false(不推荐entry或false)
useBuiltIns: "usage",
// corejs 版本(设置了useBuiltIns的话,corejs是必配的)
corejs: 3,
// 目标浏览器 preset-env必须设置,或者利用.browerslistrc文件替代
targets: [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}]
]
}
如果上面 useBuiltIns 设置的是 entry,那么需要在入口文件中手动引入 polyfill。
另外根据 corejs 设置的版本不同,引入 polyfill 的方式也不同。如下:
// corejs: 2
import '@babel/polyfill'
// corejs: 3
import "core-js/stable"
import "regenerator-runtime/runtime"
transform-runtime
当然,我们知道,babel 已经不推荐直接导入 polyfill 的方式,转而推荐使用@babel/plugin-transform-runtime 插件。
// babel.config.js
module.exports = {
presets: [
["@babel/preset-env", {
// 采用 EsModule
modules: false,
targets: [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}]
]
plugins: [
["@babel/plugin-transform-runtime", {
// useBuiltIns属性已被默认设置
// 推荐corejs设置3,因为这个版本支持实例属性
corejs: 3
}]
]
}
另外,要注意的是,根据 corejs 的版本,需要安装对应的 runtime-corejs。
# corejs: 2
yarn add @babel/runtime-corejs2
# corejs: 3
yarn add @babel/runtime-corejs3