@babel/plugin-transform-runtime

博客:姜瑞涛的官方网站
原文链接:https://www.jiangruitao.com/babel/transform-runtime/
版权采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议 转载需注明原文作者、链接与版权协议

本节主要讲@babel/plugin-transform-runtime以及@babel/runtime。

在我们用Babel做语法转换的时候(注意,这里是单纯的做语法转换,暂时不使用polyfill补齐API),需要Babel在转换后的代码里注入一些函数才能正常工作,先看一个例子。

github配套代码是babel13的例子。

Babel配置文件如下,用@babel/preset-env做语法转换:

  {
    "presets": [
      "@babel/env"
    ],
    "plugins": [
      
    ]
  }

转换前的代码使用了ES6的class类语法:

  class Person {
    sayname() {
      return 'name'
    }
  }

  var john = new Person()
  console.log(john)

Babel转码后生成的代码如下:

  "use strict";

  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

  function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

  var Person = /*#__PURE__*/function () {
    function Person() {
      _classCallCheck(this, Person);
    }

    _createClass(Person, [{
      key: "sayname",
      value: function sayname() {
        return 'name';
      }
    }]);

    return Person;
  }();

  var john = new Person();
  console.log(john);

可以看到转换后的代码上面增加了好几个函数声明,这就是注入的函数,我们称之为辅助函数。@babel/preset-env在做语法转换的时候,注入了这些函数声明,以便语法转换后使用。

但样这做存在一个问题。在我们正常的前端工程开发的时候,少则几十个js文件,多则上千个。如果每个文件里都使用了class类语法,那会导致每个转换后的文件上部都会注入这些相同的函数声明。这会导致我们用构建工具打包出来的包非常大。

那么怎么办?一个思路就是,我们把这些函数声明都放在一个npm包里,需要使用的时候直接从这个包里引入到我们的文件里。这样即使上千个文件,也会从相同的包里引用这些函数。通过webpack这一类的构建工具打包的时候,我们只会把使用到的npm包里的函数引入一次,这样就做到了复用,减少了体积。

@babel/runtime就是上面说的这个npm包,@babel/runtime把所有语法转换会用到的辅助函数都集成在了一起。

我们先安装这个包:

  npm install --save @babel/runtime
  npm install --save-dev @babel/cli @babel/core  @babel/preset-env

然后到node_modules目录下看一下这个包结构

_classCallCheck, _defineProperties与 _createClass这个三个辅助函数就在图片所示的位置,我们直接引入即可。

github配套代码是babel13a的例子。

我们手动把辅助函数替换掉函数声明,之前文件的代码就变成如下所示:

  "use strict";

  var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
  var _defineProperties = require("@babel/runtime/helpers/defineProperties");
  var _createClass = require("@babel/runtime/helpers/createClass");

  var Person = /*#__PURE__*/function () {
    function Person() {
      _classCallCheck(this, Person);
    }

    _createClass(Person, [{
      key: "sayname",
      value: function sayname() {
        return 'name';
      }
    }]);

    return Person;
  }();

  var john = new Person();
  console.log(john);

这样就解决了代码复用和最终文件体积大的问题。不过,这么多辅助函数要一个个记住并手动引入,平常人是做不到的,我也做不到。这个时候,Babel插件@babel/plugin-transform-runtime就来帮我们解决这个问题。

@babel/plugin-transform-runtime有三大作用,其中之一就是自动移除语法转换后内联的辅助函数(inline Babel helpers),使用@babel/runtime/helpers里的辅助函数来替代。这样就减少了我们手动引入的麻烦。

github配套代码是babel13b的例子。

现在我们除了安装@babel/runtime包提供辅助函数模块,还要安装Babel插件@babel/plugin-transform-runtime来自动替换辅助函数:

  npm install --save @babel/runtime
  npm install --save-dev @babel/cli @babel/core  @babel/preset-env @babel/plugin-transform-runtime

现在,我们的Babel配置文件如下:

  {
    "presets": [
      "@babel/env"
    ],
    "plugins": [
      "@babel/plugin-transform-runtime"
    ]
  }

转换前a.js代码:

  class Person {
    sayname() {
      return 'name'
    }
  }

  var john = new Person()
  console.log(john)

执行"npx babel a.js -o b.js"命令后,转换生成的b.js里代码如下:

  "use strict";

  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

  var Person = /*#__PURE__*/function () {
    function Person() {
      (0, _classCallCheck2["default"])(this, Person);
    }

    (0, _createClass2["default"])(Person, [{
      key: "sayname",
      value: function sayname() {
        return 'name';
      }
    }]);
    return Person;
  }();

  var john = new Person();
  console.log(john);

可以看到,它生成的代码比我们完全手动引入@babel/runtime里的辅助函数更加优雅。实际前端开发的时候,我们除了安装@babel/runtime这个包外,一定会安装@babel/plugin-transform-runtime这个Babel插件包的。

下一节接着讲@babel/plugin-transform-runtime的使用。

注:

1.每个转换后的文件上部都会注入这些相同的函数声明,那为何不用webpack一类的打包工具去掉重复的函数声明,而是要单独再引一个辅助函数包?

webpack在构建的时候,是基于模块来做去重工作的。每一个函数声明都是引用类型,在堆内存不同的空间存放,缺少唯一的地址来找到他们。所以webpack本身是做不到把每个文件的相同函数声明去重的。因此我们需要单独的辅助函数包,这样webpack打包的时候会基于模块来做去重工作。

posted @ 2020-07-19 15:03  姜瑞涛  阅读(4947)  评论(0编辑  收藏  举报