webpack+vue項目在IE下報Promise未定義錯誤的踩坑

低版本瀏覽器引起的問題

一個基于webpack+babel+vue的項目,一般本地是在chrome瀏覽上面開發(fā),chrome瀏覽器開發(fā)因為支持大部分新的js特性,所以一般不怎么需要polyfill, 比如Promise,string實例的includes方法等。即使在低版本瀏覽器中,通過babel-runtime的polyfill也是可以轉(zhuǎn)換的,但是事不竟然,項目在IE9瀏覽器上報錯,錯誤如下截圖:

image.png

很明顯,項目中使用了Promise,在請求數(shù)據(jù)的時候也是使用Promise來封裝的ajax方法的,請求數(shù)據(jù)就可以的,在我們進行項目優(yōu)化的時候,我們進行了對項目按需加載模塊來打包,現(xiàn)在導致了IE9又不支持該新特性,所以導致報錯。

那么, 問題來了,babel-runtime不是會自動polyfill項目中的Promise功能么,為啥沒有呢?下面就來一探究竟。

babel-runtime真的幫我們轉(zhuǎn)換了么

按照babel官網(wǎng)的介紹,babel-runtimebabel-polyfill一樣,都是對不支持的新功能進行polyfill,只是:

  • babel-runtime: 他不會污染全局環(huán)境,會在局部進行polyfill,另外不會轉(zhuǎn)換一些實例方法,如'abc'.includes('a'),其中的includes方法就不會翻譯。它一般結合babel-plugin-transform-runtime來使用。

  • babel-polyfill:簡單粗暴,他會污染全局環(huán)境,比如在不支持Promise的瀏覽器會polyfill一個全局的Promise對象供調(diào)用;另外,不支持的實例方法也在對應的構造函數(shù)原型鏈上添加要polyfill的方法。

那么上面例子中的Promise,babel-runtime真的幫我們轉(zhuǎn)換了么,在項目中測試一下,發(fā)下它確實轉(zhuǎn)換了。

  let _promise = new Promise()

如上,在代碼中測試一下,查看對應的轉(zhuǎn)換文件:

image

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

Promise未定義錯誤真兇

既然babel-runtime會對經(jīng)過babel編譯的代碼進行代碼轉(zhuǎn)換,那么可以猜想:

錯誤的真正原因是一些代碼沒有經(jīng)過babel-runtime編譯轉(zhuǎn)換。

首先想到的是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.");

這樣的情況需要主要,經(jīng)過排查,在本項目中,沒有發(fā)現(xiàn)是因為npm包引起的。那么還有一種可能:webapck本身產(chǎn)生的一些代碼

通過定位錯誤發(fā)生地方,發(fā)現(xiàn)確實是webpack自身產(chǎn)生的代碼需要Promise。在webpack的官網(wǎng)也找到了答案

image

可以發(fā)現(xiàn),在webpack使用異步加載模塊時, require.ensure需要原生支持Promise,因為我們項目是按需加載,所以才導致上面問題的產(chǎn)生。即:

webpack生成的new Promise相關代碼, 超出babel的babel-runtime的控制范圍,只有polyfill全局的Promise才能解決此問題。

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

在vue項目的main.js文件加上這句話
// 將Promise拋出為全局對象
window.Promise = Promise
image.png

然后babel-runtime會將其轉(zhuǎn)化為如下:

// 將Promise拋出為全局對象
window.Promise = __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_promise___default.a()

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

在跨瀏覽器中的選擇

  • 對于跨瀏覽器的項目,尤其是低版本的IE時,建議選擇babel-polyfill, 它可以對靜態(tài)或者實例方法都會轉(zhuǎn)換

  • 對于指定的瀏覽器的項目如chrome,直接使用babel-runtime來進行轉(zhuǎn)換,它不會對實例方法進行轉(zhuǎn)換。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容