1. 程式人生 > >webpack+babel專案在IE下報Promise未定義錯誤引出的思考

webpack+babel專案在IE下報Promise未定義錯誤引出的思考

低版本瀏覽器引起的問題

最近開發一個基於webpack+babel+react的專案,一般本地是在chrome瀏覽上面開發,chrome瀏覽器開發因為支援大部分新的js特性,所以一般不怎麼需要polyfill, 比如Promise,string例項的includes方法等。即使在低版本瀏覽器中,通過babel-runtime的polyfill也是可以轉換的,但是事不竟然,專案在IE9瀏覽器上報錯,錯誤如下截圖:

很明顯,專案中使用了Promise,但是IE9又不支援該新特性,所以導致報錯。

那麼, 問題來了,babel-runtime不是會自動polyfill專案中的Promise

功能麼,為啥沒有呢?下面就來一探究竟。

babel-runtime真的幫我們轉換了麼

按照babel官網的介紹,babel-runtimebabel-polyfill一樣,都是對不支援的新功能進行polyfill,只是:

  • babel-runtime: 他不會汙染全域性環境,會在區域性進行polyfill,另外不會轉換一些例項方法,如'abc'.includes('a'),其中的includes方法就不會翻譯。它一般結合babel-plugin-transform-runtime來使用。

  • babel-polyfill:簡單粗暴,他會汙染全域性環境,比如在不支援Promise的瀏覽器會polyfill一個全域性的Promise物件供呼叫;另外,不支援的例項方法也在對應的建構函式原型鏈上新增要polyfill的方法。

那麼上面例子中的Promise,babel-runtime真的幫我們轉換了麼,在專案中測試一下,發下它確實轉換了。

  let _promise = new Promise()

如上,在程式碼中測試一下,檢視對應的轉換檔案:

可以看到,在專案中,babel-runtime真的幫我們進行了polyfill,那為啥還會報上面的Promise未定義的錯誤呢???

Promise未定義錯誤真凶

既然babel-runtime會對經過babel編譯的程式碼進行程式碼轉換,那麼可以猜想:

錯誤的真正原因是一些程式碼沒有經過babel-runtime編譯轉換

首先想到的是node_modules模組,因為一些npm包在webpack配置中不需要babel的編譯,而這些包可能需要Promise的原生支援功能.

vuex,之前就有人在github上提出過類似的問題vuex requires a promise polyfill in this browser。因為在它原始碼裡面是這樣判斷的:

assert(typeof Promise !== 'undefined', "vuex requires a Promise polyfill in this browser.");

這樣的情況需要主要,經過排查,在本專案中,沒有發現是因為npm包引起的。那麼還有一種可能:webapck本身產生的一些程式碼

通過定位錯誤發生地方,發現確實是webpack自身產生的程式碼需要Promise。在webpack的官網也找到了答案

可以發現,在webpack使用非同步載入模組時, require.ensure需要原生支援Promise,因為我們專案是按需載入,所以才導致上面問題的產生。即:

webpack生成的new Promise相關程式碼, 超出babel的babel-runtime的控制範圍,只有polyfill全域性的Promise才能解決此問題。

解決上面的問題, 大部分人會想到使用其他Promise的polyfill庫,如babel-polyfill或者es6-promise等,這固然是一個解決辦法,但是可以結合babel-runtime的轉換功能來為全域性Promise進行polyfill,不會引入額外的庫。程式碼如下:

// 將Promise丟擲為全域性物件
window.Promise = Promise

我實際是install 一下babel-runtime,

然後babel-runtime會將其轉化為如下:

// 將Promise丟擲為全域性物件
window.Promise = __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_promise___default.a()

這樣,將babel-runtime的Promise的polyfill掛到window下,達到其他Promise的polyfill的效果。

在跨瀏覽器中的選擇

本人的大部分後臺專案,一般會要求使用人員使用chrome瀏覽器,只選擇babel-runtime就可以滿足需求,因為chrome大部分js新特性都支援,如字串例項的includes, 雖然babel-runtime不會編譯,但是瀏覽器自己會支援,不會產生問題。但是對於跨瀏覽器的專案就需要特別考慮了。

  • 對於跨瀏覽器的專案,尤其是低版本的IE時,建議選擇babel-polyfill, 它可以對靜態或者例項方法都會轉換

  • 對於指定的瀏覽器的專案如chrome,直接使用babel-runtime來進行轉換,它不會對例項方法進行轉換

參考文獻

1、webpack文件
2、babel的polyfill和runtime的區別
3、babel原理和polyfill和runtime的區別
4、webpack+babel+transform-runtime, IE下提示Promise未定義?
5、ES6 + Webpack + React + Babel 如何在低版本瀏覽器上愉快的玩耍(下)