一、現(xiàn)狀
近些年,隨著移動(dòng)端的發(fā)展,前端技術(shù)也發(fā)生了翻天覆地的變化,尤其是ECMAScript標(biāo)準(zhǔn)的不斷升級(jí),從es2015一直到最新的es2021,各種各樣的前端javascript的特性被定義和推廣,各個(gè)瀏覽器的廠商為了市場(chǎng)占比和用戶體驗(yàn)的提升也在進(jìn)行著一輪一輪的優(yōu)化升級(jí),目前的主流瀏覽器已經(jīng)支持大部分的es2015的特性。在面向c端用戶,移動(dòng)作為主流的今天,PC端的C端用戶量及使用頻率都在急劇萎縮,但是對(duì)于面向PC端的B端用戶為主的站點(diǎn),移動(dòng)化似乎無(wú)法替代PC。這里面仍然牽涉著很多兼容性問題,尤其是一些B端企業(yè)用戶仍然使用著IE這一即將被歷史淘汰的產(chǎn)品,但是面對(duì)現(xiàn)實(shí),我們無(wú)法選擇,必須需要支持,畢竟每個(gè)客戶都是上帝。下面先來(lái)看一組相關(guān)統(tǒng)計(jì)數(shù)據(jù),了解一下目前市場(chǎng)瀏覽器占比情況,為今天的主題介紹做一個(gè)鋪墊。
近五年瀏覽器占比google趨勢(shì)圖

statcounter統(tǒng)計(jì)瀏覽器占比圖

參考:https://gs.statcounter.com/
CanIUse上IE11瀏覽器占比

參考:https://caniuse.com/?search=es6
從以上三圖可知,IE的占比越來(lái)越低,尤其IE11的瀏覽器占比只有目前大約0.7%。但是我們還是必須面對(duì)問題,下面一起來(lái)看一下問題:
二、問題
廢話不說(shuō),上圖:

以上為在我項(xiàng)目中用戶瀏覽器報(bào)錯(cuò)圖,報(bào)錯(cuò)直接導(dǎo)致用戶瀏覽器頁(yè)面白屏,下面我們來(lái)看一下收集來(lái)的信息。
直接獲取信息
- 對(duì)象不支持"includes"屬性和方法
- 語(yǔ)法錯(cuò)誤:=>、class
用戶反饋信息
- 操作系統(tǒng):windows
- 瀏覽器:IE11
問題描述
- 由報(bào)錯(cuò)得知代碼對(duì)IE11有兼容性問題
- 由報(bào)錯(cuò)具體信息得知IE11對(duì)includes、=>、class有兼容性有問題
問題推斷
初步推斷:IE11瀏覽器解析不了es6語(yǔ)法和api,至少不支持以上語(yǔ)法和api。下面讓我們來(lái)一起排查驗(yàn)證一下問題推斷。
三、排查
首先,讓我們打開我們常用css和js兼容性檢查平臺(tái)https://caniuse.com/?search=es6,來(lái)看一下能否通過它來(lái)查到es6在IE11的兼容性情況。

果然,如圖所示,文案提示我們IE11對(duì)es6的語(yǔ)法支持有限,我們?cè)賮?lái)打開一下對(duì)es6詳細(xì)語(yǔ)法/api有一個(gè)詳細(xì)統(tǒng)計(jì)的github統(tǒng)計(jì)es6兼容性站點(diǎn)看一下:

如圖所示,上面有詳細(xì)的es6+具體語(yǔ)法/api在各種瀏覽器上的的兼容情況,IE11那一列尤其明顯,由圖可知IE11僅能支持es6語(yǔ)法/api的約11%的特性,根據(jù)橫坐標(biāo)特效名稱和IE11的交叉行可查看具體兼容詳情。驚喜發(fā)現(xiàn),看圖中Compilers/polyfills列,竟然已經(jīng)給出了相應(yīng)提升es6兼容性的方案,其中Babel7 +Core-js3竟然可以支持到72%的es特性,再詳細(xì)看includes、=>、class可以做到絕大多數(shù)的支持。
至此,我們排查確認(rèn)出了IE11對(duì)es6特性的兼容性只有11%導(dǎo)致了項(xiàng)目問題的產(chǎn)生,也找出了初步解決方案:Babel7 + Core-js3,下面來(lái)一起看一下相應(yīng)方案。
四、方案
初步方案:Babel7 + Core-Js3
1、引入babel7
需要注意的是,引入babel7之后,由于babel默認(rèn)只轉(zhuǎn)換js語(yǔ)法(如=>,class等),不轉(zhuǎn)換新的api(比如:Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局對(duì)象,以及一些定義在全局對(duì)象上的方法:Object.assign等),所以需要引入轉(zhuǎn)換新api的工具/庫(kù)。
2、引入core-js3
想要兼容/轉(zhuǎn)換es6+的新api,就需要core-js3來(lái)支持了。
core-js可以有效支持promises、symbols、collections、iterators、typed arrays 、ECMAScript 7+ proposals、setImmediate等。
這里面有些需要注意的事項(xiàng)是,轉(zhuǎn)換新api的方案叫做polyfill,babel-polyfill是兼容新版api的延續(xù)至近的一套方案,它內(nèi)部引入了core-js2,又集成了regenerator-runtime,但是使用這套方案會(huì)把全部api特性都打包,會(huì)導(dǎo)致未使用的新api的引入及文件過大和全局污染。有沒有可以按需加載的方案呢?答案是有:babel-runtime。
babel-runtime不同與babel-polyfill的是它可以結(jié)合babel插件babel-plugin-transform-runtime,按需引入babel-runtime中的polyfill,但是babel-runtime也有其缺點(diǎn),它只支持靜態(tài)方法,includes、Object.assign等實(shí)例下es6方法支持不足,解決這個(gè)問題就需要相應(yīng)插件來(lái)支持了。
3、babel插件化
剛剛說(shuō)到babel插件化,babel有很多解決特定問題的插件,比如:babel-plugin-array-includes 、@babel/plugin-transform-arrow-functions、 babel/plugin-proposal-class-properties等,但是這些插件如果一個(gè)一個(gè)引入也是很麻煩的,有沒有可以一起引入所有插件的方法呢?
4、babel插件整合方案
最早期的插件整合方案常用的叫babel-preset-es2015,它解決了最早期es6特性的兼容性問題,但是隨著es6+的各種特性的出現(xiàn),后面由擴(kuò)展出了babel-preset-stage-1、babel-preset-stage-2、babel-preset-stage-2、babel-preset-stage-3,具體差別和功能不做贅述,大家可以自行了解一下。隨著以上方案的擴(kuò)展,慢慢babel-preset-es2015需要一個(gè)最新的集成方案,于是就有了babel-preset-env,就是那個(gè)每次npm安裝babel-preset-2015時(shí)都提醒我們的babel-preset-env安裝提醒,它可以根據(jù)目標(biāo)瀏覽器或者運(yùn)行環(huán)境配置來(lái)自動(dòng)轉(zhuǎn)換es2015+的代碼。
5、babel配置
關(guān)于babel配置,vue項(xiàng)目中有很多地方都可以配置處理,如packge.json、babel.config.js、.babelrc、.browserlistrc。大家根據(jù)實(shí)際情況配置和查看相應(yīng)文件里面的配置。
這里面需要注意的是,從babel7開始,.babelrc的作用范圍僅限于當(dāng)前項(xiàng)目,默認(rèn)不再作用于 node_modules 和工作區(qū) (./packages/*),如果需要,可以指定作用范圍。一般通過babel.config.js和.browserlistrc來(lái)做babel的轉(zhuǎn)譯處理了。具體的配置,這里不做詳細(xì)解讀了,官網(wǎng)有比較詳細(xì)的解釋:https://www.babeljs.cn/docs/config-files。
以下是可供參考的簡(jiǎn)單配置:
1、babel.config.js
module.exports = {
env: {
test: {
presets: [
[
'@babel/preset-env',
{
targets: {node: 'current'},
}
]
]
}
},
presets: ['@vue/cli-plugin-babel/preset', '@babel/preset-env'],
};
2、.browserlistrc
> 0.25%
last 2 versions
6、初步成果
通過以上相關(guān)的引入和配置,項(xiàng)目?jī)?nèi)的es6+代碼轉(zhuǎn)換沒有問題了,但是還是遺留了兩個(gè)新的問題:
- 1、頁(yè)面內(nèi)引入的外鏈js的兼容問題怎么辦?
- 2、項(xiàng)目引入的外部模塊的es6+代碼的轉(zhuǎn)換怎么辦?
完善方案
首先,對(duì)于第一個(gè)問題,如果有操作權(quán)限的話可以通過外部項(xiàng)目js的轉(zhuǎn)譯處理,如果沒有權(quán)限的情況下,保證項(xiàng)目js引入的順序,確保在存在外部es6+特性代碼的js之前實(shí)現(xiàn)polyfill的注冊(cè)兼容處理,比如可以在html中實(shí)現(xiàn)includes的定義等。
其次,對(duì)于第二個(gè)問題,由于babel默認(rèn)不會(huì)轉(zhuǎn)換node_modules中的文件,需要我們針對(duì)處理。
對(duì)于vue-cli3之前或者webpack項(xiàng)目中,可以結(jié)合includes、excludes的優(yōu)先級(jí)順序,配置需要在項(xiàng)目中需要轉(zhuǎn)譯的外部包;對(duì)于vue-cli3.x之后的項(xiàng)目可以通過配置vue.config.js中的transpileDependencies屬性來(lái)實(shí)現(xiàn)對(duì)外部包的轉(zhuǎn)譯處理,這里可以看一下vue官網(wǎng)對(duì)它的一個(gè)簡(jiǎn)單解釋:https://cli.vuejs.org/zh/config/#runtimecompiler
。
這里是我的項(xiàng)目中的一個(gè)簡(jiǎn)單示例:
module.exports = {
…,
transpileDependencies: [
/node_modules[/\\\\](@antv|)[/\\\\]/,
/node_modules[/\\\\](lodash|)[/\\\\]/,
/node_modules[/\\\\](hex-rgb|)[/\\\\]/,
/node_modules[/\\\\](d3-dispatch|)[/\\\\]/,
/node_modules[/\\\\](d3-force|)[/\\\\]/,
/node_modules[/\\\\](regl|)[/\\\\]/,
/node_modules[/\\\\](ml-matrix|)[/\\\\]/
],
…,
}
五、總結(jié)

對(duì)于vue項(xiàng)目,我們分項(xiàng)目?jī)?nèi)的代碼兼容及引入的外部js和引入的外部模塊三部分來(lái)分別解決IE11的問題,這里面沒有詳細(xì)說(shuō)的一點(diǎn)是,使用babel配置時(shí),注意瀏覽器的兼容比例,由于IE11的市場(chǎng)占比目前在0.7%左右,所以瀏覽器的占比數(shù)值需要是小于0.7的值,我項(xiàng)目中取值為0.25,不過這里就會(huì)增大的項(xiàng)目轉(zhuǎn)譯之后的代碼量,具體大家可以根據(jù)實(shí)際情況配置處理,感謝大家的查閱,謝謝!