最近開發一個基於webpack+babel+vue
的項目,一般本地是在chrome瀏覽上面開發,chrome瀏覽器開發因為支持大部分新的js特性,所以一般不怎么需要polyfill, 比如Promise,string實例的includes方法等。即使在低版本瀏覽器中,通過babel-runtime
的polyfill也是可以轉換的,但是事不竟然,項目在IE9瀏覽器上報錯,未定義“Promise”
很明顯,項目中使用了Promise
,但是IE9又不支持該新特性,所以導致報錯。
那么, 問題來了,babel-runtime
不是會自動polyfill項目中的Promise
功能么,為啥沒有呢?下面就來一探究竟。
babel-runtime真的幫我們轉換了么
按照babel官網的介紹,babel-runtime
跟babel-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
然后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
來進行轉換,它不會對實例方法進行轉換。
vue 解決ie9的兼容問題
當vue遇見ie9的時候,部署到服務器之后,打開居然是一片空白,vue是支持ie9的,這個時候就需要來一波兼容了
1、首先npm install --save babel-polyfill
2、然后在main.js
中的最前面引入babel-polyfill
import 'babel-polyfill'
3、在index.html 加入以下代碼(非必須)
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
4、在config中的webpack.base.conf.js
中,修改編譯配置
entry:{ app:['babel-polyfill','./src/main.js'] }
本地文件:E:\www\svn\project\vue_manage_test
當然,如果你只用到了 axios
對 promise
進行兼容,可以只用 es6-promise
npm install es6-promise --save
在 main.js
中的最前面 引入
import 'es6-promise/auto'
完成以上配置,ie9兼容就完成了