@babel/plugin-transform-runtime 有什么用
用于解决两个问题:
- 避免在多文件重复输出helper方法造成冗余,使helper方法变成引用公共依赖
@babel/runtime-corejs3
- babel会将编译前代码中的ES新特性进行全局代理,从而实现polyfill。当我们在写一个独立工具库依赖给到业务应用使用的时候,这时独立工具库中全局代理代码,就会影响到业务应用的代码了,比如工具库和业务应用里,都对Array.prototype.includes进行了不一样了代理;
@babel/plugin-transform-runtime
会把polyfill再进行一个沙箱处理,从而避免相互影响。
babel转义代码分两步,一个是语法转义,一个是api转义;
简单的语法转义比如const -> var , 但是复杂的语法转义还需要借助babel的helper方法。比如例子代码:
js
class Classes {
constructor(){}
fn(){}
}
会转义为:
js
"use strict";
function _classCallCheck(instance, Constructor) { //... }
function _createClass(Constructor, protoProps, staticProps) { //... }
// 省略N行
var Classes = /*#__PURE__*/function () {
function Classes() {
_classCallCheck(this, Classes);
}
_createClass(Classes, [{
key: "fn",
value: function fn() {}
}]);
return Classes;
}();
其中_createClass
就是babel
的helper
方法,会输入到对应的编译好的文件中;那么就会有个问题,假如100个文件,里面都用了class语法,就会导致所有的文件都会输入helper方法,当跑在同一个应用时,这100个文件就会产生冗余重复99次helper方法。而用了@babel/plugin-transform-runtime
就可以把helper指向公共依赖@babel/runtime-corejs3/helpers/xxx
方法,减少转码后的文件大小。当配置babel plugin 加上@babel/plugin-transform-runtime
,编译结果就或变为:
js
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass"));
var Foo = /*#__PURE__*/function () {
function Foo() {
(0, _classCallCheck2["default"])(this, Foo);
}
(0, _createClass2["default"])(Foo, [{
key: "saySth",
value: function saySth() {}
}]);
return Foo;
}();
babel 配置:
js
module.exports = {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage",
"corejs": 3
},
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3 // 指定 runtime-corejs 的版本,目前有 2 3 两个版本
}
]
]
};
再说babel解决的另一个问题,babel会引用corejs对ES特性进行polyfill,比如以下代码会被处理:
js
const arr = [1,2,3,4];
const flag = arr.includes(2);
以上为例子1
处理为:
js
"use strict";
require("core-js/modules/es.array.includes.js");
var arr = [1, 2, 3, 4];
var flag = arr.includes(2);
我们看一下core-js/modules/es.array.includes.js
:
js
'use strict';
var $ = require('../internals/export');
var $includes = require('../internals/array-includes').includes;
var fails = require('../internals/fails');
var addToUnscopables = require('../internals/add-to-unscopables');
// FF99+ bug
var BROKEN_ON_SPARSE = fails(function () {
// eslint-disable-next-line es/no-array-prototype-includes -- detection
return !Array(1).includes();
});
// `Array.prototype.includes` method
// https://tc39.es/ecma262/#sec-array.prototype.includes
$({ target: 'Array', proto: true, forced: BROKEN_ON_SPARSE }, {
includes: function includes(el /* , fromIndex = 0 */) {
return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
}
});
// https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
addToUnscopables('includes');
代码其实很简单,大致就是对Array.prototype.includes 通过definePrototy
等方法进行定义polyfill。这里会改动到Array
的原型。
这里做个实验,把编译后的代码和core-js/modules/es.array.includes.js
中includes改为includes1,确定core-js/modules/es.array.includes.js
是定义了原型:
js
// test.js
require("core-js/modules/es.array.includes.js");
var arr = [1, 2, 3, 4];
var flag = arr.includes1(2);
console.log(flag); // true
console.log(Array.prototype.includes1); // [Function: includes1]
// core-js/modules/es.array.includes.js
// ...
$({ target: 'Array', proto: true, forced: BROKEN_ON_SPARSE }, {
includes1: function aincludes(el /* , fromIndex = 0 */) {
console.log(11111111111); // 111111111
return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
}
});
// ...
无论任何环境原生情况都是没有Array.prototype.includes1的,执行test.js顺利打印出es.array.includes.js
中includes1定义,证明babel通过对原型属性拦截进行polyfill。
在单次编译里面可以认为改对象原型是没问题的,但是如果这次编译的结果被业务应用使用到了,并且业务应用也有对对象原型相同的属性的修改,这是就会冲突了。那babel是如何解决这个问题的呢?
通过@babel/plugin-transform-runtime
会把polyfill改为从@babel/runtime-corejs3
中获取,并且原本includes写法改为另一种形式,从而避免污染Array方法;
js
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
var arr = [1, 2, 3, 4];
var flag = (0, _includes["default"])(arr).call(arr, 2);