Skip to content
On this page

babel-polyfill与transform-runtime

概念

babel-polyfill是一个工具库吗?可以这么辅助理解,但它不仅仅是一个工具库,它存在的意义甚于工具。

它的作用是为应用提供es2015+的运行环境,注入式的hack重写了原生方法。

transform-runtime是一个babel插件(babel-plugin-transform-runtime),它的作用和polyfill类似,但它不会注入式的hack,而是重命名实现这个方法。另外,它所重写的方法是有限的,不是所有的es2015+方法都重写。

实践分析

1.transform-runtime

查看该插件源码后你会发现它的本质是babel-runtime。我们用vue的会发现,初始化项目时,.babelrc中默认带上了插件"transform-vue-jsx", "transform-runtime",查看文档你会发现transform-runtime是支持传递options的

"helpers": true,
"polyfill": true,
"regenerator": true,
"moduleName": "babel-runtime"

细心的你肯定发现了该选项中也包含了polyfill,还有helpers,regenerator。这些默认都是true,那作用如何呢?

我们在概念中提到它是不会重写覆盖原生方法的,举个例子:我们在代码中使用Promise,打包后如下:

  // 打包前
  created() {
    new Promise()
  }
  // 打包后
  created: function created() {
    new promise_default.a();
  }

关于promise_default

// EXTERNAL MODULE: ./node_modules/babel-runtime/core-js/promise.js
var promise = __webpack_require__("//Fk");
var promise_default = /*#__PURE__*/__webpack_require__.n(promise);

我们会发现它引用自core-js,好了打住。我们知道polyfill的作用也是hack,那它与babel-polyfill的区别是什么?

当我们查看网上的资料后都会发现:transform-runtime只会提供对象静态的或全局的方法,不提供实例方法,例如Array.prototype.includes

我们需要再理解一层,也就是说transform-runtime不会提供原型上的方法,我们可以利用obj.hasOwnProperty(pro)来检测该方法是否为对象自身属性。

Array.hasOwnProperty('find') // false
Array.hasOwnProperty('true') // true

这样就好理解了,当我们开启polyfill: true这个选项时,就知道哪些方法可以直接使用而不用引用babel-polyfill了。

关于regenerator,我用的最多的是async await了,它的作用是实现 generators、yield、async 及 await 等相关的 polyfills。

关于helpers,是babel内部自身用到的工具方法,设置为helpers: true时,就会统一的去引用babel-runtime里的方法,而不是在每个文件中都重写一遍工具方法,举例:

// helpers为false,你会在每个文件中看到
var _typeof = typeof symbol_default.a === "function" && typeof ...
var _extends = assign_default.a || function (target) { for (var i = ...

// helpers为true
// EXTERNAL MODULE: ./node_modules/babel-runtime/helpers/typeof.js
var helpers_typeof = __webpack_require__("pFYg");
var typeof_default = /*#__PURE__*/__webpack_require__.n(helpers_typeof);

// EXTERNAL MODULE: ./node_modules/babel-runtime/helpers/extends.js
var helpers_extends = __webpack_require__("Dd8w");
var extends_default = /*#__PURE__*/__webpack_require__.n(helpers_extends);

2.babel-polyfill

能发挥它最大用处的用法是结合babel-preset-env使用,结合使用后会根据.babelrc里的

"targets": {
  "browsers": ["iOS >= 7"]
},

按需引入,利用useBuiltIns 属性来决定是在入口处统一引入还是在使用的地方引入。

注意,当我们使用babel-polyfill后,肯定不需要再使用transform-runtime里的polyfill,我们应当置为false。

结合case来实践

case 1:useBuiltIns: false,在任一文件引入import "babel-polyfill",打包后

// EXTERNAL MODULE: ./node_modules/babel-polyfill/lib/index.js
var lib = __webpack_require__("j1ja");
var lib_default = /*#__PURE__*/__webpack_require__.n(lib);

你会发现此处的引用变成内部引用的一个模块,名为j1ja,然后我们找到这个模块

/***/ "j1ja":
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(global) {

__webpack_require__("4M2W");

__webpack_require__("zkX4");

__webpack_require__("Wwne");

if (global._babelPolyfill) {
  throw new Error("only one instance of babel-polyfill is allowed");
}
global._babelPolyfill = true;
// ....

我们会发现这个模块在vendor.js中定义的。

case 2: useBuiltIns: true,在任一文件引入import "babel-polyfill",打包后

// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.typed.array-buffer.js
var es6_typed_array_buffer = __webpack_require__("9mmO");
var es6_typed_array_buffer_default = /*#__PURE__*/__webpack_require__.n(es6_typed_array_buffer);

// EXTERNAL MODULE: ./node_modules/core-js/modules/es6.typed.int8-array.js
var es6_typed_int8_array = __webpack_require__("52Wt");
var es6_typed_int8_array_default = /*#__PURE__*/__webpack_require__.n(es6_typed_int8_array);

会发现有很多模块在此处引入,我们查找下这个被引入的模块定义,同样也存在于vendor.js中。

useBuiltIns的作用就是决定模块是否在使用处引入。

总结

我们会发现,他们都是对于core-js的引入,只不过引入的多少与最终造成的结果不一样。

其实很多时候,我们也可以直接使用core-js,我们查看repo地址https://github.com/zloirock/core-js#babelpolyfill

发现对于polyfillbabel-runtime 有单独的解释

babel-polyfill是对于core-js的引用,而babel-runtime 是对于core-js-pure的引用。

其实很多时候更适合单独使用core-js

import 'core-js/features/array/from'; // <- at the top of your entry point
import 'core-js/features/array/flat'; // <- at the top of your entry point
import 'core-js/features/set';        // <- at the top of your entry point
import 'core-js/features/promise';    // <- at the top of your entry point

参考
https://github.com/zloirock/core-js
https://www.babeljs.cn/docs/usage/polyfill/
https://babeljs.io/docs/en/next/babel-polyfill.html
https://github.com/babel/babel-preset-env
https://zhuanlan.zhihu.com/p/29058936