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 插件有三个作用:

  1. 替换内联辅助函数
  2. 替换全局 API
  3. 引入 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/polyfillcore-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
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容