在處理瀏覽器兼容性問題以前,咱們先來看一下如今的瀏覽器市場份額是怎樣的,👇下面是來自statCounter的數據,基本上覆蓋了全世界瀏覽器市場份額的統計,並且前端er常用的caniuse所拉取的瀏覽器數據就是來自statCounter。javascript
從統計數據能夠看出,對於國內的PC端瀏覽器,QQ瀏覽器以及Sogou的佔比仍是挺高的,因此在作兼容性處理的時候也須要多考慮這兩款瀏覽器。不過QQ瀏覽器和Sogou都是封裝了Chrome的內核,而它們的更新一般也就是隨着Chrome的版本更新而更新,可是這兩款瀏覽器一般不會和Chrome的最新版本同步,而是會有必定的滯後性,因此一般咱們兼容的時候要去考慮它們所使用的Chrome內核版原本處理。html
隨着尤大最新力做-Vue CLI3的發佈,團隊也將項目的腳手架升級爲最新的CLI3了,CLI3帶來了不少新的特性,好比支持webpack四、支持可視化化地配置項目、封裝了不少官方的插件下降了上手的成本等。其中還有對瀏覽器兼容性的支持-結合了社區最新的工具以及現代模式。而CLI 3.0的瀏覽器兼容性處理,主要分爲三個部分。browserlist、polyfill以及modern mode(如今模式)。前端
指定項目的目標瀏覽器範圍,這個值會被@babel/preset-env和Autoprefixer等工具用來肯定須要轉譯的JS特性以及須要添加前綴的CSS特性。vue
{
"browserslist": [
"last 1 version", //表示最新一個版本
]
}
複製代碼
Browserslist本質是對一系列瀏覽器的queries,查詢瀏覽器的版本, 它會從caniuse中拉取數據來查詢!它的好處是給予了開發者一個標準的地方存放項目支持的瀏覽器版本,他們能夠在配置中找到這個支持版本。java
下面是用法解釋node
下面要要介紹的是Vue CLI3所用到的Babel預設- @Babel/preset-env,它是在CLI3中指導Babel進行轉譯的一個工具,提及Babel就不得不提一下轉譯,和其餘語言中的編譯不一樣,轉譯是相似以下圖所示的一個過程:webpack
總的來講,靜態語言的編譯好比Java是將源代碼編譯爲字節碼,這兩種一般是處於不一樣抽象層次的語言,而Transpile的兩端本質上是相同層次上的抽象,它通常是從一種支持更高級特性的好比ES2017的實現,轉爲一種語言特性較少的好比ES5的實現。在解決瀏覽器兼容性時,很大程度上是須要依賴Babel轉譯來解決低版本瀏覽器不支持某些新特性的狀況。git
CLI3如何使用browserslist以及babel進行轉譯es6
babel是一個編譯JS文件的和工具。用於編譯新JS新特性到支持的目標瀏覽器。babel/preset-env從browserlistrc文件中加載目標瀏覽器:github
"babel": {
"presets": [
[
"@babel/preset-env"
]
]
}
複製代碼
而後babel會把新標準中的JS語法特性編譯到當前目標瀏覽器支持的代碼:
const array = [1, 2, 3];
const [first, second] = array;
複製代碼
output:
const array = [1, 2, 3];
const first = array[0],
second = array[1];
複製代碼
此處的const特性,由於該目標瀏覽器支持了,因此不須要降級處理。
** Babel的轉譯能夠簡單地用如下三個階段歸納: **
不一樣的特性,結合Browserslist以及@Babel/preset-env會根據當前的目標瀏覽器支持的特性的狀況來轉譯,不支持的特性再去降級處理。Babel自己是個複雜的話題,後續有餘力結合編譯原理再去深挖一下。
Polyfill一般是在特性檢測後,來決定是否須要引入的一段JS代碼,它是用基於目標瀏覽器支持的代碼編寫的。
if(browserSupportAllFeatures()) {
main();
} else {
loadScript('polyfills.js', main);
}
複製代碼
原理 默認狀況下,它會把useBuiltIns: 'usage' 傳遞給 babel/preset-env,這樣它會根據源代碼中出現的語言特性自動檢測須要的polyfill。 確保了最終包裏的polyfill數量最小化。然而,這也意味着若是其中一個依賴須要特殊的polyfill,默認狀況下babel沒法檢測出來。
// 源代碼
var a = new Promise();
複製代碼
// 輸出
import 'core-js/modules/es6.promise';
var a = new Promise();
複製代碼
依賴須要Polyfills 若是有依賴須要polyfill,你有幾種選擇:(來自babel-preset-app)
trasnpileDependencies: [lodash,...]
複製代碼
// babel.config.js
module.exports = {
presets: [
['@vue/app', {
polyfills: [
'es6.promise',
'es6.symbol'
],
useBuiltIns: 'entry'
}]
]
}
複製代碼
推薦 這種方式添加polyfills而不是在源代碼中直接導入它們,由於若是這裏列出的polyfill在browserflist的目標不須要,則它會被自動排除。 3. 若是該依賴交付了ES5 代碼,但使用了ES6+ 特性且沒有顯式列出須要的polyfill,請使用useBuiltIns: 'entry' 而後在入口文件添加 import '@babel/polyfill'。 這會根據browserlist 目標導入全部polyfill,這就不須要擔憂依賴的polyfill問題了, 缺點:可是包含了一些沒有用到的polyfill因此最終的包可能會增長。(這也沒辦法,由於依賴沒有顯式列出須要的polyfill)
// 源代碼
import '@babel/polyfill'
複製代碼
// 輸出
import 'core-js/modules/es7.string.pad-start';
import 'core-js/modules/es7.string.pad-end';
複製代碼
什麼是現代模式? 現代模式是Vue CLI3提出的,爲了解決支持更老的瀏覽器而爲它們交付笨重的代碼的問題,使用官方所給的腳手架搭建好項目後,能夠像下面這樣運行:
vue-cli-service build --modern
複製代碼
CLI3內置的Service服務會調用webpack構建編譯生成兩種類型的包,一種是現代版的包,它不會引入額外的polyfill。 一種是舊版的包,面向不支持的舊版瀏覽器,它會根據browserslist以及babel-preset-env的配置引入額外的polyfill。
<script type="module">
在被支持的瀏覽器中加載;它們還會使用<link rel ="modulepreload">
進行預加載。<script nomodule>
加載,會被支持ESM的瀏覽器忽略。輸出對比 未使用現代模式:
Vue CLI 3 現代模式:
使用了modern的方式,會在目錄下生成了兩種包:
解釋
<script src="/xx/sxx.js" nomodule></script>
複製代碼
script標籤中聲明瞭nomodule特性的,會告訴支持ES2015 Module的瀏覽器,不要去執行這個腳本。這種標籤一般就是在老舊瀏覽器中使用的,引用的是帶有legacy標識的腳本。
<script type="module" src="/xxx/sss/a.js"></script>
複製代碼
當script標籤中聲明瞭type特性的值爲module時,瀏覽器就會把裏面的代碼視做一整個JS模塊,腳本內容的處理不受charset和defer屬性的影響,代碼的行爲可能會與不指定爲module時表現得不一樣。 (之前type的值通常是JS MIME值,在符合HTML5的瀏覽器,表示腳本爲JS。但如今H5規範要求開發者省略這個屬性而不提供冗餘的MIME類型。)
Vue CLI 3提供了一整套的解決方案,裏面提供了社區以及Vue官方的工具,包含Browserslist、Babel、@Babel/preset-env以及現代模式來共同處理瀏覽器兼容性這個大坑。CLI3構建出的工程會利用Browserslist去進行瀏覽器查詢(包括類型和版本),而後結合預設對Babel進行配置共同來針對目標環境對源代碼進行轉譯或引入Polyfills來解決兼容性問題。Moreover,現代模式一樣會根據這些設定以及目標環境去構建出適合運行在支持項目全部新特性的瀏覽器以及不支持特性的瀏覽器的兩種包。最後,預告一下,下一篇文章會結合Modernizr和CLI3來進行實踐 ---- 《瀏覽器兼容性實踐-Mondernizr篇》