現狀
我們想要用 ES6 語法來寫 JavaScript。然而由於我們需要兼容老版本的瀏覽器,那些瀏覽器不支持 ES6,我們需要解決這個問題。
有一個標准的做法是:寫 ES6 代碼 → 將所有代碼編譯成 ES5 的(比如通過 Babel)→ 再將編譯后的代碼加載到瀏覽器執行。
這可能已經不再是最有效率的方式了。因為用這種方式,我們強制最新的瀏覽器運行舊代碼,實際上它們完全可以運行最新的代碼。它們支持 ES6,我們難道不能直接給它們 ES6 代碼嗎?
改進方式
有一個 polyfill 項目叫做 Polyfill.io API,它可以通過 polyfill 方式在客戶端執行 ES6 代碼。

它也實現了一些 HTML 特性的 polyfill,比如 <picture> 元素。
下面是他們網站的描述:
Polyfill.io 讀取每個請求的 User-Agent(UA) 頭,並生成適合於該瀏覽器的 polyfill ,基於你的應用所使用的特性發回必要的代碼。[...]
Financial Times 在開發和維護這個項目,所以我們能確信這個項目可以持續更新下去,不會死掉。
有一點需要明白:Polyfill.io 沒有提供語法糖支持。比如 類、增強的對象字面量,以及箭頭函數之類的特性。對那些代碼,你仍然需要進行編譯。
配置 Polyfill.io
Adding Polyfill.io to your project can be this simple. Add the CDN-hosted script to your page:
要添加 Polyfill.io 到你的項目里非常簡單。將托管在 CDN 的腳本添加到你的頁面上:
`<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>`
運行腳本將返回 UA 和你想要的特性。
-
UA detected: chrome/ 56.0.0
-
Features requested: default
修改請求參數
它提供了一堆選項 來自定義你要返回的特性。
Features
該參數指定需要 polyfill 的瀏覽器特性。多個特性名之間用逗號分隔。允許使用的特性明在 瀏覽器和特性 頁中列出。
`<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch"></script>`
在 Safari 10 下,腳本返回內容如下:
-
Features requested: fetch
-
-
- setImmediate, License: CC0 (required by "Promise", "fetch")
-
- fetch
如果一個特性,比如 fetch 依賴於另一個特性比如 Promise,Polyfill.io 會自動加載依賴。
Flags
- always - Polyfill 將始終被包含,不管 UA 中指出的瀏覽器是否已經支持該特性。
- gated - 通過特性檢測來判斷 Polyfill,只有在瀏覽器原生 API 不支持這些特性的情況下才返回並執行 Polyfill。
`<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch&flags=gated"></script>`
Callback
Polyfill 腳本加載完成之后要執行的函數名。這是最簡單的在 polyfill 加載完成后觸發你自己的代碼的方式,這樣 polyfill 服務可以更容易通過 async 和 defer 屬性被異步加載。
存在的問題
聽起來很好,但是仍然不完美。
最新的瀏覽器不需要加載 ES5 代碼了,但是還需要通過服務器請求來檢測是否需要 polyfill。
這非常困擾我,因此我正在做一個小項目來改進它。
一個更好的方式
配置 dynamic polyfill
我創建了一個叫做 dynamic-polyfill 的 npm 包。它在發起任何服務端請求前檢測特性是否已經被原生支持。
它的配置看起來如下:
-
import polyfill from 'dynamic-polyfill'
-
-
polyfill({
-
fills: 'fetch, Promise',
-
options: 'gated', // default: null
-
minify: false, // default: true
-
afterFill() {
-
main()
-
}
-
})
-
-
function main() {
-
// app code here
-
}
簡單來說,它的執行過程如下:
檢測 [window.fetch, window.Promise] 是否存在。
如果存在,運行 afterFill() 回調。
如果它們不存在,創建一個 <script> 標簽,並且包含 async 屬性,將 Polyfill.io 鏈接插入,用參數中提供的選項去請求,並在它加載完成后執行 afterFill() 回調。
注意: 現在還沒有支持全部選項,只有那些最重要的項被支持。
腳本很小
由於這個模塊在壓縮后不到 1KB 大小,而且沒有任何依賴,對項目使用來說成本超低。
結論
為最新的瀏覽器寫不過時和有效率的 JavaScript,讓 Polyfill.io 去處理老版本的的瀏覽器,如果不必要,別發起額外的 HTTP 請求。
