babel-runtime使用与性能优化

16,529 阅读3分钟

文章概览

本文主要内容包括:什么是babel-runtime、如何使用、使用场景与限制、如何结合babel-polyfill进行性能优化。

本文所有例子可以在 笔者的github 找到。

什么是babel-runtime

在文章《babel-polyfill使用与性能优化》中,笔者对babel-polyfill进行了介绍。

引入babel-polyfill会有一定副作用,比如:

  • 引入了新的全局对象:比如Promise、WeakMap等。
  • 修改现有的全局对象:比如修改了Array、String的原型链等。

在应用开发中,上述行为问题不大,基本可控。但如果在库、工具的开发中引入babel-polyfill,则会带来潜在的问题。

举个例子,我在项目中定义了跟规范不一致的Array.from()函数(别管我为什么不一样,就是这么任性),同时引入了一个库(依赖babel-polyfill),此时,这个库可能覆盖了自定义的Array.from()函数,导致出错。

这就是babel-runtime存在的原因。它将开发者依赖的全局内置对象等,抽取成单独的模块,并通过模块导入的方式引入,避免了对全局作用域的修改(污染)。

因此,如果是开发库、工具,可以考虑使用 babel-runtime。

入门例子

首先,安装依赖。babel-plugin-transform-runtime用于构建过程的代码转换,babel-runtime是实际导入项目代码的功能模块。

npm install babel-cli --save-dev
npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime

接着,创建 index.js。

// index.js
var promise = new Promise;

最后,运行 babel 转换代码,

`npm bin`/babel index.js

转换结果如下。没有用到全局的 Promise 对象,而是导入babel-runtime/core-js/promise模块。

import _Promise from "babel-runtime/core-js/promise";
var promise = new _Promise();

技术实现细节

babel-plugin-transform-runtime 插件做了如下事情,下文分别举例说明。

  1. core-js aliasing:自动导入babel-runtime/core-js,并将全局静态方法、全局内置对象 映射到对应的模块。
  2. Helper aliasing:将内联的工具函数移除,改成通过babel-runtime/helpers模块进行导入,比如_classCallCheck工具函数。
  3. Regenerator aliasing:如果你使用了 async/generator 函数,则自动导入 babel-runtime/regenerator模块。

下面分别举例进行说明。

core-js aliasing

1、安装依赖。

npm install babel-cli --save-dev
npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime

2、创建index.js。

// index.js
var promise = new Promise;

var arr = Array.from('foo');

3、创建.babelrc。

{
  "plugins": ["transform-runtime"]
}

4、运行转换命令。

`npm bin`/babel index.js

5、转换后的代码。

import _Array$from from 'babel-runtime/core-js/array/from';
import _Promise from 'babel-runtime/core-js/promise';
var promise = new _Promise();

var arr = _Array$from('foo');

helper aliasing

1、安装依赖

npm install --save-dev babel-cli 
npm install --save-dev babel-plugin-transform-es2015-classes 
npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime

2、创建index.js。

// index.js
class Person {}

3、创建.babelrc。

{
  "plugins": [
    "transform-es2015-classes",
    "transform-runtime"
  ]
}

4、运行转换命令

`npm bin`/babel index.js

5、转码结果如下:

import _classCallCheck from "babel-runtime/helpers/classCallCheck";

let Person = function Person() {
  _classCallCheck(this, Person);
};

regenerator-aliasing

1、安装依赖:

npm install --save-dev babel-cli
npm install --save-dev babel-plugin-transform-runtime
npm install --save-dev babel-plugin-transform-regenerator

2、创建index.js。

// index.js
function * run () {}

3、创建.babelrc。

{
  "plugins": [
    "transform-regenerator",
    "transform-runtime"
  ]
}

4、运行转换命令。

`npm bin`/babel index.js

5、转码结果如下。

import _regeneratorRuntime from "babel-runtime/regenerator";

var _marked = /*#__PURE__*/_regeneratorRuntime.mark(run);

function run() {
  return _regeneratorRuntime.wrap(function run$(_context) {
    while (1) switch (_context.prev = _context.next) {
      case 0:
      case "end":
        return _context.stop();
    }
  }, _marked, this);
}

性能优化

前面章节提到,babel-plugin-transform-runtime 主要做了三件事情:core-js aliasing、Helper aliasing、Regenerator aliasing。

它们都可以通过配置进行开关,默认配置如下:

{
  "plugins": [
    ["transform-runtime", {
      "helpers": true,
      "polyfill": true,
      "regenerator": true,
      "moduleName": "babel-runtime"
    }]
  ]
}

还是前面的代码:

// index.js
var promise = new Promise;

var arr = Array.from('foo');

默认配置下,使用 babel-plugin-transform-runtime 的转换结果如下。虽然避免了修改全局对象,但是也引入了不少冗余的导入代码。

import _Array$from from 'babel-runtime/core-js/array/from';
import _Promise from 'babel-runtime/core-js/promise';
var promise = new _Promise();

var arr = _Array$from('foo');

修改配置文件.babelrc,将 polyfill 配置参数设置为 false:

{
  "plugins": [
    ["transform-runtime", {
      "polyfill": false
    }]
  ]
}

再看下转换结果:

var promise = new Promise();

var arr = Array.from('foo');

备注:实际项目中,记得自行引入polyfill。

使用场景与限制

babel-runtime一般用于两种场景:开发库/工具、移除冗余工具函数(helper function)。

因为它不会对实例方法进行修改(比如Array.prototype.includes()),因此,在这种场景下需要使用 babel-polyfill。

相关链接

babeljs.io/docs/plugin…