前段時間遇到了一個vue-cli致使的bug,鑽研了半天才解決,因而有空寫篇文章記錄下來。
有用戶反饋在ie11下打不開咱們的線上網站,即便開了兼容模式也打不開咱們的網站,頁面白屏。
css
初步判斷,因爲css樣式資源、頁面資源都已經加載到位,排除網絡環境問題後,讓用戶打開控制檯截圖看一下,白屏的緣由是因爲JS執行報錯阻塞了後續的邏輯執行和渲染。html
代碼中存在執行錯誤的邏輯,可是按照打包後的壓縮js文件是徹底沒法肯定問題的,若是你有接入sentry或者badjs等錯誤上報模塊,那麼你能夠根據錯誤信息,快速定位到源碼上的問題。
可是問題又出現了,sentry的引入其實最合法合規最正確的引入方式,應該是最頁面資源加載的最前面,有些人會其防止在head標籤中優先加載,這樣頁面後續不管是資源加載出錯、HTML解析錯誤 或者 JS邏輯執行報錯,均可以捕獲到而且進行上報。
然而當初沒有考慮到這一點,而是直接在vue-cli中的main.js中直接import sentry的方式引入,這樣意味着打包後的bundle若是首次執行出錯的話,sentry都未能完成初始化,致使錯誤內容沒法上報。
根據用戶的截圖也不能很快的肯定問題代碼以及報錯內容,因而先讓有windows電腦的同事用ie11打開網址,查看報錯內容,果真,報錯了,報的錯還不止一個。vue
定位對應的行代碼:
node
第二個問題主要是因爲使用v-model綁定type爲radio值的input時,還自定義了屬性:checked,致使最後編譯出來的代碼裏出現對重複屬性的定義,而正式環境下的bundle包通常都是默認'use strict',因此致使觸發嚴格模式,ie11報錯,根據屬性名即可以快速定位問題並修改。webpack
最後仍是回來說講第一個問題,智障IE還不支持class?是的 不支持
可是 爲啥咱們的打包產物裏會出現莫名其妙的ES6代碼?
按理說vue-cli已經幫咱們引入了babel-loader針對ES6代碼進行了轉換,爲啥還會出現class呢?
帶着這個問題咱們繼續往下看es6
根據報錯文件chunk-vendors,咱們來開一下咱們的vue.config.js中針對不一樣chunk的配置文件:web
optimization: { splitChunks: { cacheGroups: { echarts: { name: 'chunk-echarts', test: /[\\/]node_modules[\\/]echarts[\\/]/, chunks: 'all', priority: 10, reuseExistingChunk: true, enforce: true, }, demo: { name: 'chunk-demo', test: /[\\/]src[\\/]views[\\/]demo[\\/]/, chunks: 'all', priority: 20, reuseExistingChunk: true, enforce: true, }, page: { name: 'chunk-page', test: /[\\/]src[\\/]/, chunks: 'all', priority: 10, reuseExistingChunk: true, enforce: true, }, vendors: { name: 'chunk-vendors', test: /[\\/]node_modules[\\/]/, chunks: 'all', priority: 5, reuseExistingChunk: true, enforce: true, }, }, }, },
出現es6代碼都來自於node_modules下的chunk包,爲啥呢?
接下來爲了進一步肯定咱們的es6代碼是從哪一個第三方庫引入的,咱們須要暫時關閉掉uglify壓縮代碼,配置以下:vue-cli
module.exports = { ... optimization: { minimize: false, ... } };
再進行以上配置,便可以關閉webpack4的默認壓縮配置,這時候咱們再來咱們打包產物中找找,大片的const和let,還有咱們要找的罪魁禍首,class:npm
對應的第三方庫的地址爲./node_modules/dom7/dist/dom7.modular.js
json
接下來咱們要繼續追查,是哪裏引用到了這個包,在package-lock.json裏很快鎖定到目標:
swiper是移動端用戶輪播的一個第三方庫,而這裏引用到了dom7模塊,
咱們翻一下swiper的源碼 在它的源碼中的swiper.esm.bundle.js打包產物中,的確看到這麼一條引入:
import { $, addClass, removeClass, hasClass, toggleClass, attr, removeAttr, data, transform, transition as transition$1, on, off, trigger, transitionEnd as transitionEnd$1, outerWidth, outerHeight, offset, css, each, html, text, is, index, eq, append, prepend, next, nextAll, prev, prevAll, parent, parents, closest, find, children, remove, add, styles } from 'dom7/dist/dom7.modular';
可是爲啥咱們引入的是esm的產物代碼呢?默認webpack不會幫咱們引入main字段對應的產物嗎?
翻看一下webpack的官方文檔,咱們能夠看到有這麼一個配置項:
resolve.mainFields:
當從 npm 包中導入模塊時(例如,import * as D3 from "d3"),此選項將決定在 package.json 中使用哪一個字段導入模塊。根據 webpack 配置中指定的 target 不一樣,默認值也會有所不一樣。
webpack的默認配置下,mainfields字段所指定的是優先以module爲入口,這麼設計的緣由是爲了順應時代的潮流,讓你們使用es6導出的模塊,逐步淘汰掉以往的Commonjs模塊規範,畢竟import、export能帶來更多的好處。
而咱們的vue-cli在構建編譯 默認target是爲node,因此咱們的mainFields字段也默認爲['module','main']
。
到這裏咱們已經明白問題的關鍵是啥了,因爲webpack不會對node_modules中引入的第三方庫內的代碼進行二次的ES6轉碼處理,而webpack默認又會引入module字段所指向的打包產物,module產物通常是ES6規範輸出,main產物通常是commonjs或UMD規範輸出,因此部分ES6代碼的殘留致使IE11不兼容,最終致使報錯。
居然肯定了問題所在,接下來咱們只須要按照vue-cli官方文檔所介紹,自行配置一下webpack配置便可:
configureWebpack: { resolve: { mainFields: ['main', 'module'], },
vue-cli中默認引入了一些loader和plugin,因此內部已經有一份webpack的配置了,vue-cli經過暴露configureWebpack參數的方式,容許使用者對webpack進行再配置,若是是以對象形式傳入,會與內部的webpack配置對象進行merge操做。
修改完配置後,最後檢查一下咱們的打包產物,的確已經沒有了const、let、class等這類es6語法,大功告成,bug解決。
固然,若是你的網站須要兼容大多數瀏覽器和不一樣場景的話,你還須要爲你的代碼引入polyfill,畢竟ES6轉ES5只是針對部分語法,然而ES6所新增的部分API是不會進行轉換的,這時候只能經過引入前置polyfill的方式來達到兼容。
謝謝觀看~