1. 程式人生 > >按需載入babel-polyfill

按需載入babel-polyfill

babel-polyfill

Babel預設只轉換JS語法,而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全域性物件,以及一些定義在全域性物件上的方法(比如Object.assign)都不會轉碼。

舉例來說,ES2015在Array物件上新增了Array.from方法。Babel 就不會轉碼這個方法。如果想讓這個方法執行,必須使用babel-polyfill。(內部集成了core-js和regenerator)

npm install babel-polyfill --save

使用時,在所有程式碼執行之前增加require('babel-polyfill')

。或者更常規的操作是在webpack.config.js中將babel-polyfill作為第一個entry。因此必須把babel-polyfill作為dependencies而不是devDependencies。

注意: 在你的整個應用裡只使用一次require("babel-polyfill")。多次importrequirebabel-polyfill會引起報錯,因為它可能導致全域性衝突和其他難以追蹤的問題。 我們建議建立一個只包含require語句的單個入口檔案。

babel-polyfill的缺點

  1. 使用後打包後的體積很大,因為babel-polyfill是一個整體,把所有方法都加到原型鏈上。比如我們只使用了Array.from
    ,但它把Object.defineProperty也給加上了,這就是一種浪費了。
  2. babel-polyfill會汙染全域性變數,給很多類的原型鏈上都作了修改,如果我們開發的也是一個類庫供其他開發者使用,這種情況就會變得非常不可控。

解決方法1:單獨引入

可以通過單獨使用core-js的某個類庫來解決,比如通過引入babel-runtime/core-js/promise來獲取Promise

但是這樣需要我們認為判斷並且手動引入類庫,太麻煩了。

解決方法2:使用babel-runtime和babel-plugin-transform-runtime

安裝:

npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime

然後在.babelrc中:

{
  "plugins": ["transform-runtime"]
}

啟用外掛babel-plugin-transform-runtime後,Babel就會使用babel-runtime下的工具函式,將Promise重寫成_Promise(只是打比方),然後引入_Promise helper函式。這樣就避免了重複打包程式碼和手動引入模組的痛苦。

由於採用了沙盒(Sandbox)機制,不會汙染全域性變數,同時也不會去修改內建類的原型,帶來的壞處是它不會polyfill原型上的擴充套件(例如 Array.prototype.includes()不會被polyfill,Array.from()則會被polyfill)

解決方法3:使用babel-preset-env

在Babel7中引入了babel-preset-env,根據你支援的環境自動決定適合你的Babel外掛。

npm install babel-preset-env --save-dev

在沒有任何配置選項的情況下,babel-preset-env與babel-preset-latest(或者babel-preset-es2015,babel-preset-es2016和babel-preset-es2017一起)的行為完全相同。

下面例子包含了支援每個瀏覽器最後兩個版本和safari大於等於7版本所需的polyfill和程式碼轉換:

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      }
    }]
  ]
}

如果你目標開發Node.js而不是瀏覽器應用的話,你可以配置babel-preset-env僅包含特定版本所需的polyfill和transform:

{
  "presets": [
    ["env", {
      "targets": {
        "node": "6.10"
      }
    }]
  ]
}

按需載入babel-polyfill的關鍵是useBuiltIns選項,預設值為false,它的值有三種:

  • false: 不對polyfills做任何操作
  • entry: 根據target中瀏覽器版本的支援,將polyfills拆分引入,僅引入有瀏覽器不支援的polyfill
  • usage(新):檢測程式碼中ES6/7/8等的使用情況,僅僅載入程式碼中用到的polyfills

這個選項可以啟用一個新的外掛來替換語句import "babel-polyfill"或者require("babel-polyfill")以及基於瀏覽器環境的babel-polyfill個性化需求。

我們需要將選項值設為usage,然後它會在每個JS檔案執行,分析根據每個檔案用到的語言特性匯入相關的polyfill,例如

import "core-js/modules/es6.promise";
var a = new Promise();

當然分析可能會有錯誤,例如:

import "core-js/modules/es7.array.includes";
a.includes // assume a is an []

babel-preset-env會假設a是陣列,所以會匯入相關的es7的includes方法

這樣我們就真正實現了按需載入,會讓我們打包後的程式碼大大減小。

參考