@babel/plugin-transform-runtime 有什么用

@babel/plugin-transform-runtime 有什么用

用于解决两个问题:

  1. 避免在多文件重复输出helper方法造成冗余,使helper方法变成引用公共依赖 @babel/runtime-corejs3
  2. 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 就是babelhelper方法,会输入到对应的编译好的文件中;那么就会有个问题,假如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);

参考文档

相关推荐
只喜欢赚钱的棉花没有糖13 分钟前
http的缓存问题
前端·javascript·http
小小小小宇28 分钟前
请求竞态问题统一封装
前端
loriloy29 分钟前
前端资源帖
前端
源码超级联盟31 分钟前
display的block和inline-block有什么区别
前端
GISer_Jing37 分钟前
前端构建工具(Webpack\Vite\esbuild\Rspack)拆包能力深度解析
前端·webpack·node.js
让梦想疯狂39 分钟前
开源、免费、美观的 Vue 后台管理系统模板
前端·javascript·vue.js
海云前端1 小时前
前端写简历有个很大的误区,就是夸张自己做过的东西。
前端
葡萄糖o_o1 小时前
ResizeObserver的错误
前端·javascript·html
AntBlack1 小时前
Python : AI 太牛了 ,撸了两个 Markdown 阅读器 ,谈谈使用感受
前端·人工智能·后端
MK-mm2 小时前
CSS盒子 flex弹性布局
前端·css·html