Babel 进阶二
@babel/plugin-transform-runtime
插件用来替换内联辅助函数,替换全局 API,引入 regeneratorRuntime 函数。
@babel/runtime
工具包提供内联辅助函数,引入 regeneratorRuntime 函数。
@babel/runtime-corejs2
工具包提供内联辅助函数,提供非全局的 core-js2 版本的 API,引入 regeneratorRuntime 函数。
@babel/runtime-corejs3
工具包提供内联辅助函数,提供非全局的 core-js3 版本的 API,引入 regeneratorRuntime 函数。
@babel/runtime
、@babel/runtime-corejs2
、@babel/runtime-corejs3
这三个工具包是用来配合 @babel/plugin-transform-runtime
插件使用的,@babel/plugin-transform-runtime
插件根据配置会自动调用这三个工具包。
通常根据需要安装其中一个工具包即可。
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime
插件有三个作用:
- 替换内联辅助函数
- 替换全局 API
- 引入 regeneratorRuntime 函数
替换内联辅助函数
在 Babel 在使用 @babel/preset-env
进行语法转换的时候(不是补充 API),常常会注入一些辅助函数。例如转换 ES6 的 Class 语法的时候:
配置文件:
const presets = ['@babel/preset-env']
const plugins = []
module.exports = {
presets,
plugins
}
转换前:
class Person {
sayname() {
return 'name'
}
}
var john = new Person()
console.log(john)
转换后:
"use strict";
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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
var Person = /*#__PURE__*/function () {
function Person() {
_classCallCheck(this, Person);
}
_createClass(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
var john = new Person();
console.log(john);
可以看到转换后的代码上面增加了好几个函数声明,这就是注入的函数,我们称之为辅助函数。
在我们正常的前端工程开发的时候,少则几十个js文件,多则上千个。如果每个文件里都使用了class类语法,那会导致每个转换后的文件上部都会注入这些相同的函数声明。这会导致我们用构建工具打包出来的包非常大。
我们把这些辅助函数声明都放在一个npm包里,需要使用的时候直接从这个包里引入到我们的文件里。这样即使上千个文件,也会从相同的包里引用这些函数。通过webpack这一类的构建工具打包的时候,我们只会把使用到的npm包里的函数引入一次,这样就做到了复用,减少了体积。
@babel/runtime/helpers
包提供辅助函数,安装 @babel/preset-env
的时候会自动安装 @babel/runtime
,不过在项目开发的时候,我们一般都会再单独安装一遍 @babel/runtime
。
我们手动替换掉辅助函数声明,之前文件的代码就变成如下所示:
"use strict";
var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
var _defineProperties = require("@babel/runtime/helpers/defineProperties");
var _createClass = require("@babel/runtime/helpers/createClass");
var Person = /*#__PURE__*/function () {
function Person() {
_classCallCheck(this, Person);
}
_createClass(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
var john = new Person();
console.log(john);
这样就解决了代码复用和最终文件体积大的问题。不过,这么多辅助函数要一个个记住并手动引入太麻烦了,@babel/plugin-transform-runtime
插件就来帮我们解决这个问题。
@babel/plugin-transform-runtime
有三大作用,其中之一就是自动移除语法转换后内联的辅助函数(inline Babel helpers),使用 @babel/runtime/helpers
里的辅助函数来替代。这样就减少了我们手动引入的麻烦。
现在我们除了安装 @babel/runtime
包提供辅助函数模块,还要安装 Babel 插件 @babel/plugin-transform-runtime
。
npm i -D @babel/plugin-transform-runtime
npm i @babel/runtime
配置文件:
const presets = ['@babel/preset-env']
const plugins = [
[
'@babel/plugin-transform-runtime',
{
helpers: true
}
]
]
module.exports = {
presets,
plugins
}
转换前代码:
class Person {
sayname() {
return 'name'
}
}
var john = new Person()
console.log(john)
转换后代码:
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var Person = /*#__PURE__*/function () {
function Person() {
(0, _classCallCheck2["default"])(this, Person);
}
(0, _createClass2["default"])(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
var john = new Person();
console.log(john);
可以看到,Babel 转码之后,@babel/plugin-transform-runtime
插件自动替换掉了所有的内联辅助函数,转为引用 @babel/runtime
包中的辅助函数。这样在通过 Webpack 打包的时候,不管有多少个 js 文件使用了辅助函数,打包之后的代码里只引入一次相应的辅助函数。
替换全局 API
通过引入 @babel/polyfill
或者 core-js/stable
,可以补齐全局的 API,但是这样会对全局环境产生污染。例如 Promise API,引入 @babel/polyfill
或 core-js/stable
会重写了 window.Promise 及其原型链。
如果我们不想在引入 API 的同时污染全局环境,可以配置 @babel/plugin-transform-runtime
插件,它会将全局 API 的引用替换成 @babel/runtime-corejs2
或者 @babel/runtime-corejs3
包中的非全局 API。
npm i -D @babel/plugin-transform-runtime
npm install @babel/runtime-corejs2
# 也可以安装版本 3
# npm install @babel/runtime-corejs3
配置文件:
const presets = ['@babel/env']
const plugins = [
[
'@babel/plugin-transform-runtime',
{
// 这里的版本号要与 @babel/runtime-corejs2 一致
corejs: 2
}
]
]
module.exports = {
presets,
plugins
}
转换前代码:
var obj = Promise.resolve();
转换后代码:
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var obj = _promise["default"].resolve();
可以看到转换后的代码并没有定义全局变量 Promise,而是使用 _promise["default"]
代替所有使用 Promise 的地方,这样就不会污染全局环境了。
引入 regeneratorRuntime 函数
- 通过
import 'regenerator-runtime'
可以手动引入全局 regeneratorRuntime 函数。 - 通过
import '@babel/polyfill'
可以手动引入全局 regeneratorRuntime 函数。 - 通过
@babel/preset-env
的 useBuiltIns 选项设置为 'usage',可以自动按需引入全局 regeneratorRuntime 函数。 - 通过
@babel/plugin-transform-runtime
插件的 regenerator 选项设置为 true,可以自动按需引入非全局 regeneratorRuntime 函数。
配置文件:
const presets = ['@babel/env']
const plugins = [
[
'@babel/plugin-transform-runtime',
{
regenerator: true
}
]
]
module.exports = {
presets,
plugins
}
转码之前:
function* gen () {
yield 100;
}
转码之后:
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _marked = /*#__PURE__*/_regenerator["default"].mark(gen);
function gen() {
return _regenerator["default"].wrap(function gen$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return 100;
case 2:
case "end":
return _context.stop();
}
}
}, _marked);
}
可以看到,转换后的代码使用 _regenerator["default"]
代替全局函数 regeneratorRuntime
。
babel/plugin-transform-runtime 的配置项
以下是各个配置项的默认值,如果没有设置对应的配置项,则该配置项取默认值。
const presets = ['@babel/env']
const plugins = [
[
'@babel/plugin-transform-runtime',
{
"helpers": true,
"corejs": false,
"regenerator": true,
"useESModules": false,
"absoluteRuntime": false,
"version": "7.0.0-beta.0"
}
]
]
module.exports = {
presets,
plugins
}
helpers
该项是用来设置是否要自动引入辅助函数包,默认值为 true。
- 当 helpers 为 true 的时候,会自动引用
@babel/runtime/helpers
或者@babel/runtime-corejs2/helpers
或者@babel/runtime-corejs3/helpers
来替换内联辅助函数。 - 当 helpers 为 false 的时候,不会替换内联辅助函数。
- 通常将该参数设置为 true。
corejs
该项是用来定义如何处理 ES6 新增的全局 API,默认值为 false。
- 当 corejs 为 false 的时候,不对全局 API 进行处理。
- 当 corejs 为 2 的时候,使用
@babel/runtime-corejs2
包来替换全局 API。 - 当 corejs 为 3 的时候,使用
@babel/runtime-corejs3
包来替换全局 API。 - 当 corejs 不为 false 的时候,需要手动安装
@babel/runtime-corejs2
或@babel/runtime-corejs3
。
regenerator
该项是用来定义如何处理 Generator/async 函数的,默认值为 true。
- 当 regenerator 为 true 的时候,会按需引入非全局的 regeneratorRuntime 函数。
- 当 regenerator 为 false 的时候,不会引入 regeneratorRuntime 函数。如果代码中需要该函数,则需要通过
import 'regenerator-runtime'
或者import '@babel/polyfill'
手动引入。 - 通常将该参数设置为 true。
useESModules
该项用来设置是否使用 ES6 的模块化用法,取值是布尔值,默认是fasle。在用 Webpack一类的打包工具的时候,我们可以设置为 true,以便做静态分析。
absoluteRuntime 和 version
保持默认值就可以,可以省略不填。
@babel/runtime
配合 @babel/plugin-transform-runtime
插件使用的工具包,用来提供辅助函数和 regeneratorRuntime 函数。
-
@babel/runtime/helpers
提供辅助函数 -
@babel/runtime/regenerator
通过引用regenerator-runtime
包来提供 regeneratorRuntime 函数
当我们只需要通过 @babel/plugin-transform-runtime
插件来替换内联辅助函数、按需引入 regeneratorRuntime 函数,不需要替换全局 API 的时候,就可以安装 @babel/runtime
包。
npm i -D @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-runtime
npm i @babel/runtime
安装 @babel/preset-env
的时候会自动安装 @babel/runtime
包,不过在项目开发的时候,我们一般都会手动安装一遍 @babel/runtime
。
Babel 配置文件:
const presets = ['@babel/preset-env']
const plugins = [
[
'@babel/plugin-transform-runtime',
{
helpers: true,
corejs: false,
regenerator: true
}
]
]
module.exports = {
presets,
plugins
}
@babel/runtime-corejs2
配合 @babel/plugin-transform-runtime
插件使用的工具包,用来提供辅助函数、regeneratorRuntime 函数、core-js2 版本的非全局 API。
-
@babel/runtime-corejs2/helpers
提供辅助函数 -
@babel/runtime-corejs2/core-js
提供core-js@2
版本的 API -
@babel/runtime-corejs2/regenerator
引用regenerator-runtime
包来提供 regeneratorRuntime 函数
当我们需要通过 @babel/plugin-transform-runtime
插件来替换内联辅助函数、按需引入 regeneratorRuntime 函数、替换 core-js2 版本的全局 API 的时候,就可以手动安装 @babel/runtime-corejs2
。
npm i -D @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-runtime
npm i @babel/runtime-corejs2
Babel 配置文件:
const presets = ['@babel/preset-env']
const plugins = [
[
'@babel/plugin-transform-runtime',
{
helpers: true,
corejs: 2,
regenerator: true
}
]
]
module.exports = {
presets,
plugins
}
@babel/runtime-corejs3
配合 @babel/plugin-transform-runtime
插件使用的工具包,用来提供辅助函数、regeneratorRuntime 函数、core-js3 版本的非全局 API。
-
@babel/runtime-corejs3/helpers
提供辅助函数 -
@babel/runtime-corejs3/core-js
和@babel/runtime-corejs3/core-js/stable
提供core-js@3
版本的 API -
@babel/runtime-corejs3/regenerator
引用regenerator-runtime
包来提供 regeneratorRuntime 函数
当我们需要通过 @babel/plugin-transform-runtime
插件来替换内联辅助函数、按需引入 regeneratorRuntime 函数、替换 core-js3 版本的全局 API 的时候,就可以手动安装 @babel/runtime-corejs3
。
npm i -D @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-runtime
npm i @babel/runtime-corejs3
Babel 配置文件:
const presets = ['@babel/preset-env']
const plugins = [
[
'@babel/plugin-transform-runtime',
{
helpers: true,
corejs: 3,
regenerator: true
}
]
]
module.exports = {
presets,
plugins
}