引言
babel默認只轉換新的 JavaScript 語法,比如箭頭函數、擴展運算(spread)。
不轉換新的 API,例如Iterator
、Generator
、Set
、Maps
、Proxy
、Reflect
、Symbol
、Promise
等全局對象,以及一些定義在全局對象上的方法(比如 Object.assign
)都不會轉譯。如果想使用這些新的對象和方法,則需要為當前環境提供一個墊片(polyfill)。
此篇僅對三種polyfill進行介紹,並講了他們的安裝配置。具體的每種對新語法的轉換結果,可以看參考鏈接的第一個。
三種polyfill介紹
babel-polyfill
目前最常用的配合Babel一起使用的polyfill是babel-polyfill,通過改寫全局prototype的方式實現,它會加載整個polyfill,針對編譯的代碼中新的API進行處理,並且在代碼中插入一些幫助函數,比較適合單獨運行的項目。
babel-polyfill解決了Babel不轉換新API的問題,但是直接在代碼中插入幫助函數,會導致污染了全局環境,並且不同的代碼文件中包含重復的代碼,導致編譯后的代碼體積變大。雖然這對於應用程序或命令行工具來說可能是好事,但如果你的代碼打算發布為供其他人使用的庫,或你無法完全控制代碼運行的環境,則會成為問題。
babel-runtime
Babel為了解決上述問題,提供了單獨的包babel-runtime用以提供編譯模塊的工具函數,啟用插件babel-plugin-transform-runtime后,Babel就會使用babel-runtime下的工具函數。
babel-runtime插件能夠將這些工具函數的代碼轉換成require語句,指向為對babel-runtime的引用。每當要轉譯一個api時都要手動加上require('babel-runtime')
。簡單說 babel-runtime 更像是一種按需加載的實現,比如你哪里需要使用 Promise,只要在這個文件頭部 require Promise from 'babel-runtime/core-js/promise'
就行了
不過如果你許多文件都要使用 Promise,難道每個文件都要 import 一遍不成?
babel-plugin-transform-runtime
為了方便使用 babel-runtime,解決手動 require 的苦惱。它會分析我們的 ast 中,是否有引用 babel-rumtime 中的墊片(通過映射關系),如果有,就會在當前模塊頂部插入我們需要的墊片。
transform-runtime 是利用 plugin 自動識別並替換代碼中的新特性,你不需要再引入,只需要裝好 babel-runtime 和 配好 plugin 就可以了。
好處是按需替換,檢測到你需要哪個,就引入哪個 polyfill,如果只用了一部分,打包完的文件體積對比 babel-polyfill 會小很多。而且 transform-runtime 不會污染原生的對象,方法,也不會對其他 polyfill 產生影響。
所以 transform-runtime 的方式更適合開發工具包,庫,一方面是體積夠小,另一方面是用戶(開發者)不會因為引用了我們的工具,包而污染了全局的原生方法,產生副作用,還是應該留給用戶自己去選擇。
⭐比較
babel-polyfill與babel-runtime相比雖然有各種缺點,但在某些情況下仍然不能被babel-runtime替代, 例如,
[1, 2, 3].includes(3)
,Object.assign({}
, {key: 'value'})
,Array
,Object
以及其他”實例”下es6的方法,babel-runtime是無法支持的, 因為babel-runtime只支持到 static 的方法。
安裝配置
babel-polyfill
因為這是一個 polyfill (它需要在你的源代碼之前運行),我們需要讓它成為一個 dependency,而不是一個 devDependency 。
npm install --save babel-polyfill
直接在代碼中require,或者在webpack的entry中添加,也可以在babel的env中設置useBuildins為true來開啟。
//示例:vue-cli腳手架中使用
import 'babel-polyfill'
//示例:webpack的entry中添加
entry: {
common: [
`babel-polyfill`,
`whatwg-fetch`,
`react`,
`react-dom`,
`redux`,
`react-redux`,
`js-cookie`,
],
},
babel-runtime 和 babel-plugin-transform-runtime
在大多數情況下,你應該安裝 babel-plugin-transform-runtime 作為開發依賴(使用 --save-dev),並且將 babel-runtime 作為生產依賴(使用 --save)。這個看vue-cli生成的 package.json
就能發現。
因為babel編譯es6到es5的過程中,babel-plugin-transform-runtime這個插件會自動polyfill es5不支持的特性,這些polyfill包就是在babel-runtime這個包里(core-js 、regenerator等)
npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime
用法
通過 .babelrc(推薦)
將以下內容添加到你的 .babelrc 文件中:
未包含選項:
{
"plugins": ["transform-runtime"]
}
包含選項:
{
"plugins": [
["transform-runtime", {
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}]
]
}
選項
1.輔助(helpers)
默認值是:true
表示是否開啟內聯的babel helpers(即babel或者環境本來的存在的墊片或者某些對象方法函數)(clasCallCheck,extends,etc)在調用模塊名字(moduleName)時將被替換名字。
2.墊片/polyfill
默認值是:`true'
表示是否把內置的東西(Promise,Set,Map,tec)轉換成非全局污染墊片。
3.重新生成/regenerator
默認值是:true
是否開啟generator函數轉換成使用regenerator runtime來避免污染全局域。
4.模塊名字/moduleName
默認值:babel-runtime
當調用輔助(內置墊片)設置模塊(module)名字/路徑.
例子:
{
"moduleName": "flavortown/runtime"
}
import extends from 'flavortown/runtime/helpers/extends';
優點
-
不會污染全局變量
-
多次使用只會打包一次
-
依賴統一按需引入,無重復引入,無多余引入
缺點
-
不支持實例化的方法,例
Array.includes(x)
就不能轉化 -
如果使用的API用的次數不是很多,那么transform-runtime 引入polyfill的包會比不是transform-runtime 時大
-
隨着應用的增大,相同的 polyfill 每個模塊都要做重復的工作(檢測,替換),雖然 polyfill 只是引用,編譯效率不夠高效。
參考鏈接
Babel用法 | usages transform-runtime
小毛蛋_對babel-transform-runtime,babel-polyfill的一些理解