前端工程化之Babel
1.前言
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: "entry", // 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
作者:jsgoshu
链接:https://juejin.cn/post/7019167193656999972