babel的polyfill和transform-runtime?

随着前端工程化的越发成熟,开发者需要做的事情变得更加简单了。以往需要花不少的时间去了解各种配置才能够搭起来一套前端开发环境,如果需要前后端同构的话更是让人头疼,vue-cli、next.js、nuxt.js等脚手架为我们省去了不少烦恼。

但工具的高度集成也容易让人忽略了背后的基础,就拿我自己看来说之前很长一段时间我对babel的理解就仅仅停留在,它是一个编译器,可以将ES6+编译成兼容旧版浏览器的代码,但是当我看见配置、代码中的polyfill和transform-runtime我却说不出为什么需要它们,没了会怎样?知道的越多,害怕的越少,所以今天就整理下它们到底是什么。

一开始还是习惯去google,百度根据问题关键字去查找问题,但这些答案大多是经过别人的二次消化的,也许是对的,也许是错的,很多时候无从得知,所以还是尽量从官方文档找答案吧。

什么是babel?

Babel

Babel 是一个工具链,主要用于在旧的浏览器或环境中将 ECMAScript 2015+ 代码转换为向后兼容版本的 JavaScript 代码:

  • 转换语法
  • Polyfill 实现目标环境中缺少的功能 (通过 @babel/polyfill)
  • 源代码转换 (codemods)
  • 更多!(查看视频)

从官网的介绍可知bable仅仅进行语法转换,也就是诸如const,箭头函数转换成var以及function函数声明等等,假如我们需要使用Promise以及Array.from等新的api则需要使用polyfill。

什么是babel/polyfill?

babel/polyfill

Babel includes a polyfill that includes a custom regenerator runtime and core-js.

This will emulate a full ES2015+ environment (no < Stage 4 proposals) and is intended to be used in an application rather than a library/tool. (this polyfill is automatically loaded when using babel-node).

This means you can use new built-ins like Promise or WeakMap, static methods like Array.from or Object.assign, instance methods like Array.prototype.includes, and generator functions (provided you use the regenerator plugin). The polyfill adds to the global scope as well as native prototypes like String in order to do this.

额,官方暂时还没中文的,所以这里简单翻译核心部分。

Babel的polyfill包含了regenerator runtime以及core-js。

这将能够模拟完整的ES2015+环境,而且它主要在应用中使用而不是在工具库中。(为什么呢?因为如果我们在工具库中使用了polyfill,那就意味着使用了该工具库的应用中的全局变量被polyfill所污染以及脚本体积变大,而且这是用户无法得知的,不利于按需定制。)

这意味着,你能够使用新的内建函数,比如Promise,静态方法Array.from或者Object.assign,实例方法Array.prototype.includes和generator函数(比如generators、yield、async等的实现)。这些polyfill和原生prototypes,比如 String 一样添加到了全局作用域中。

到这里可知,babel+polyfill才能够提供一套能够在旧版本浏览器环境正常运行的完整的ES2015+环境。

什么是transform-runtime?

transform-runtime

A plugin that enables the re-use of Babel’s injected helper code to save on codesize.

NOTE: Instance methods such as “foobar”.includes(“foo”) will not work since that would require modification of existing built-ins (you can use @babel/polyfill for that).

这是一个能够复用Babel注入的帮助函数的插件,通过它能够节省代码大小。

注意:实例方法比如”foobar”.includes(“foo”)无法通过transform-runtime来正常运行,因为那需要修改已存在的内建api,你可以使用polyfill来完成这项任务。

看完官方简单的介绍应该还有点模糊,下面用官方的例子再来解释下。

比如下面的代码:

1
class Person {}

通过babel编译后一般变成

1
2
3
4
5
6
7
8
9
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}

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

这里我们可以看到编译出来了一个全局的帮助函数 _classCallCheck ,这个在应用中是ok的,但如果在工具库使用就会产生以下问题:

  1. 污染了全局变量
  2. 假如工具库A和工具库B中都编译出了 _classCallCheck 这就产生了冗余重复的代码,增大了代码体积

而且上面的两个问题是用户无法感知的。如果我们使用了transform-runtime之后编译成如下代码

1
2
3
4
5
6
7
8
9
10
11
var _classCallCheck2 = require("@babel/runtime/helpers/classCallCheck");

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}

var Person = function Person() {
(0, _classCallCheck3.default)(this, Person);
};

这里我们可以看到 _classCallCheck 作为依赖引入了,而不是直接编译进入代码,transform-runtime就是提供了这么一个沙盒环境,解决了上述的两个问题。

最后,可以愉快的使用babel了吧。