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

低版本瀏覽器引發的問題

最近開發一個基於webpack+babel+react的項目,通常本地是在chrome瀏覽上面開發,chrome瀏覽器開發由於支持大部分新的js特性,因此通常不怎麼須要polyfill, 好比Promise,string實例的includes方法等。即便在低版本瀏覽器中,經過babel-runtime的polyfill也是能夠轉換的,可是事不居然,項目在IE9瀏覽器上報錯,錯誤以下截圖:javascript

很明顯,項目中使用了Promise,可是IE9又不支持該新特性,因此致使報錯。html

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

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

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

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

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

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

let _promise = new Promise()

如上,在代碼中測試一下,查看對應的轉換文件:git

能夠看到,在項目中,babel-runtime真的幫咱們進行了polyfill,那爲啥還會報上面的Promise未定義的錯誤呢???es6

Promise未定義錯誤真兇

既然babel-runtime會對通過babel編譯的代碼進行代碼轉換,那麼能夠猜測:github

錯誤的真正緣由是一些代碼沒有通過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

而後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來進行轉換,它不會對實例方法進行轉換

參考文獻

一、webpack文檔
二、babel的polyfill和runtime的區別
三、babel原理和polyfill和runtime的區別
四、webpack+babel+transform-runtime, IE下提示Promise未定義?
五、ES6 + Webpack + React + Babel 如何在低版本瀏覽器上愉快的玩耍(下)

相關文章
相關標籤/搜索