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

@babel/plugin-transform-runtime(二)

部落格:姜瑞濤的官方網站
原文連結:https://www.jiangruitao.com/docs/babel/deep/babel-plugin-transform-runtime-2/
版權採用《署名-非商業性使用-禁止演繹 4.0 國際》許可協議 轉載需註明原文作者、連結與版權協議

@babel/plugin-transform-runtime有三大作用:

1.自動移除語法轉換後內聯的輔助函式(inline Babel helpers),使用@babel/runtime/helpers裡的輔助函式來替代;

2.當代碼裡使用了core-js的API,自動引入@babel/runtime-corejs3/core-js-stable/,以此來替代全域性引入的core-js/stable;

3.當代碼裡使用了Generator/async函式,自動引入@babel/runtime/regenerator,以此來替代全域性引入的regenerator-runtime/runtime;

其中作用1已經在上一節講過,這一節我們著重來學習作用2和作用3。

作用2和3其實是在做API轉換,對內建物件進行重新命名,以防止汙染全域性環境。

在babel-polyfill一節,我們學習了引入'babel-polyfill'或'core-js/stable與regenerator-runtime/runtime'來做全域性的API補齊。但這樣可能有一個問題,那就是對執行環境產生了汙染。例如Promise,我們的polyfill是對瀏覽器的全域性物件進行了重新賦值,我們重寫了Promise及其原型鏈。

有時候我們不想改變或補齊瀏覽器的window.Promise,那麼我們就不能使用'babel-polyfill'或'core-js/stable與regenerator-runtime/runtime',因為其會對瀏覽器環境產生汙染(即修改了瀏覽器的window.Promise)。

這個時候我們就可以使用@babel/plugin-transform-runtime,它可以對我們程式碼裡ES6的API進行轉換。還是以Promise舉例子。

Babel轉換前的程式碼

  var obj = Promise.resolve();

若使用了'babel-polyfill'或'core-js/stable與regenerator-runtime/runtime'來做全域性的API補齊,那麼Babel轉換後的程式碼仍然是

  var obj = Promise.resolve();

polyfill只是補齊了瀏覽器的window.Promise物件。

若我們不使用polyfill,而開啟@babel/plugin-transform-runtime的API轉換功能。那麼Babel轉換後的程式碼將是

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

  var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

  var obj = _promise["default"].resolve();

看到效果了沒?@babel/plugin-transform-runtime把我們程式碼裡的Promise變成了_promise["default"],而_promise["default"]擁有ES標準裡Promise所有的功能。現在,即使瀏覽器沒有Promise,我們的程式碼也能正常執行。

開啟core-js相關API轉換功能的Babel配置與安裝的npm包如下

配套程式碼是github倉庫 https://github.com/jruit/babel-tutorial 的babel14例子

  {
    "presets": [
      "@babel/env"
    ],
    "plugins": [
      ["@babel/plugin-transform-runtime", {
        "corejs": 3
      }]
    ]
  }
  npm install --save @babel/runtime-corejs3
  npm install --save-dev @babel/cli @babel/core  @babel/preset-env @babel/plugin-transform-runtime

那麼,上面講的API轉換有什麼用,明明通過polyfill補齊API的方式也可以使程式碼在瀏覽器正常執行?

其實,API轉換主要是給開發JS庫或npm包等的人用的,我們的前端工程一般仍然使用polyfill補齊API。

可以想象,如果開發JS庫的人使用polyfill補齊API,我們前端工程也使用polyfill補齊API,但JS庫的polyfill版本或內容與我們前端工程的不一致,那麼我們引入該JS庫後很可能會導致我們的前端工程出問題。所以,開發JS庫或npm包等的人會用到API轉換功能。

當然,我們前端工程開發的時候也是可以使用@babel/plugin-transform-runtime的API轉換功能,畢竟沒有汙染全域性環境,不會有任何衝突。@babel/plugin-transform-runtime的預設設定下,就是對generators/async開啟了API轉換功能。

細心的你可能已經發現了,我們安裝npm包的時候,安裝的是@babel/runtime-corejs3,而上一節我們安裝的是@babel/runtime。

看名字挺像的,那麼這兩者有什麼不同呢?

在我們不需要開啟core-js相關API轉換功能的時候,我們只需要安裝@babel/runtime就可以了。上一節我們已經知道,@babel/runtime裡存放的是Babel做語法轉換的輔助函式。

在我們需要開啟core-js相關API轉換功能的時候,就需要安裝@babel/runtime的進化版@babel/runtime-corejs3。這個npm包裡除了包含Babel做語法轉換的輔助函式,也包含了core-js的API轉換函式。

除了這兩個包,還有一個@babel/runtime-corejs2的包。它和@babel/runtime-corejs3的功能是一樣的,只是裡面的函式是針對core-js2版本的。

上面的例子主要是拿Promise來講的,它屬於作用2,即對core-js的API進行轉換。其實理解了作用2,也就理解了作用3。

下面簡單說一下作用3。

在之前章節,若我們轉碼前程式碼裡有Generator函式或async函式,轉碼後需要引入'regenerator-runtime/runtime'做全域性API補齊。

全域性API補齊必然會對瀏覽器的window物件進行修改,如果我們不想要汙染window,那麼我們就不能引入'regenerator-runtime/runtime'了。

這個時候,我們可以開啟@babel/plugin-transform-runtime的作用3,對Generator/async進行API轉換。

需要注意的是,@babel/plugin-transform-runtime對Generator/async進行API轉換功能,預設是開啟的,不需要我們設定。

如何開啟或關閉@babel/plugin-transform-runtime的某個功能,除了與安裝的npm包有關,也與Babel配置檔案的配置有關,我們下一節來講。

注:

1.如果我們使用@babel/plugin-transform-runtime來做polyfill的事情,那麼就不要再使用之前講過的polyfill方式了,無論是單獨引入還是@babel/preset-env的方式。因為我們用transform-runtime來做api轉換的目的是不汙染全域性作用域。