1. 程式人生 > 實用技巧 >@babel/plugin-transform-runtime

@babel/plugin-transform-runtime

部落格:姜瑞濤的官方網站
原文連結:https://www.jiangruitao.com/docs/babel/deep/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打包的時候會基於模組來做去重工作。