按需載入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')
babel-polyfill
作為第一個entry。因此必須把babel-polyfill作為dependencies
而不是devDependencies。
注意: 在你的整個應用裡只使用一次
require("babel-polyfill")
。多次import
或require
babel-polyfill會引起報錯,因為它可能導致全域性衝突和其他難以追蹤的問題。 我們建議建立一個只包含require
語句的單個入口檔案。
babel-polyfill的缺點
- 使用後打包後的體積很大,因為babel-polyfill是一個整體,把所有方法都加到原型鏈上。比如我們只使用了
Array.from
Object.defineProperty
也給加上了,這就是一種浪費了。 - 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拆分引入,僅引入有瀏覽器不支援的polyfillusage
(新):檢測程式碼中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
方法
這樣我們就真正實現了按需載入,會讓我們打包後的程式碼大大減小。