瀏覽器兼容性
browserslist
package.json 文件里的 browserslist 字段 (或一個(gè)單獨(dú)的 .browserslistrc 文件),指定了項(xiàng)目的目標(biāo)瀏覽器的范圍。這個(gè)值會(huì)被 @babel/preset-env 和 Autoprefixer 用來確定需要轉(zhuǎn)譯的 JavaScript 特性和需要添加的 CSS 瀏覽器前綴。
現(xiàn)狀
我們想要用 ES6 語法來寫 JavaScript。然而由于我們需要兼容老版本的瀏覽器,那些瀏覽器不支持 ES6,我們需要解決這個(gè)問題。
有一個(gè)標(biāo)準(zhǔn)的做法是:寫 ES6 代碼 → 將所有代碼編譯成 ES5 的(比如通過 Babel)→ 再將編譯后的代碼加載到瀏覽器執(zhí)行。
這可能已經(jīng)不再是最有效率的方式了。因?yàn)橛眠@種方式,我們強(qiáng)制最新的瀏覽器運(yùn)行舊代碼,實(shí)際上它們完全可以運(yùn)行最新的代碼。它們支持 ES6,我們難道不能直接給它們 ES6 代碼嗎?
改進(jìn)方式
有一個(gè) polyfill 項(xiàng)目叫做 Polyfill.io API,它可以通過 polyfill 方式在客戶端執(zhí)行 ES6 代碼。
它也實(shí)現(xiàn)了一些 HTML 特性的 polyfill,比如 <picture> 元素。
下面是他們網(wǎng)站的描述:
Polyfill.io 讀取每個(gè)請(qǐng)求的 User-Agent(UA) 頭,并生成適合于該瀏覽器的 polyfill ,基于你的應(yīng)用所使用的特性發(fā)回必要的代碼。[...]
要添加 Polyfill.io 到你的項(xiàng)目里非常簡(jiǎn)單。將托管在 CDN 的腳本添加到你的頁面上:
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
運(yùn)行腳本將返回 UA 和你想要的特性。
UA detected: chrome/56.0.0
Features requested: default
@babel/polyfill模塊
@babel/polyfill模塊用于模擬完整的 ES2015+ 環(huán)境。
這意味著你可以使用諸如 Promise 和 WeakMap 之類的新的內(nèi)置組件、 Array.from 或 Object.assign 之類的靜態(tài)方法、 Array.prototype.includes 之類的實(shí)例方法以及生成器函數(shù)(generator functions)
useBuiltIns參數(shù)
npm install --save @babel/polyfill
注意,使用 --save 參數(shù)而不是 --save-dev,因?yàn)檫@是一個(gè)需要在你的源碼之前運(yùn)行的 polyfill。
我們使用 env preset 提供的 "useBuiltIns" 參數(shù),當(dāng)此參數(shù)設(shè)置為 "usage" 時(shí),就只包含你所需要的 polyfill。
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
useBuiltIns: "usage",
},
],
];
module.exports = { presets };
Babel 將檢查你的所有代碼,以便查找目標(biāo)環(huán)境中缺失的功能,然后只把必須的 polyfill 包含進(jìn)來。示例代碼如下:
Promise.resolve().finally();
將被轉(zhuǎn)換為(由于 Edge 17 沒有 Promise.prototype.finally):
require("core-js/modules/es.promise.finally");
Promise.resolve().finally();
如果我們不使用 env preset 的 "useBuiltIns" 參數(shù)(即設(shè)置為 "usage"),那么我們必須在所有代碼之前通過 require 加載一次完整的 polyfill。