最近推出的 Angular CLI 7.3 版本,新增了一個相當亮眼的特性。當你的 Angular 應用程式同時要符合 IE 或舊版瀏覽器時,以往都會用 Polyfill 來填充缺少的 HTML5/JS APIs,但這件事到了 Angular CLI 7.3 又變得更貼心了。不但如此,這個新特性一樣可以用在各種 SPA 框架中,像是 Vue, React 或其他函式庫也都可以套用相同技巧。欲知詳情請繼續看下去。

關於 polyfills.ts 檔案
在 Angular CLI 所建立的專案下,都有個 src/polyfills.ts
檔案,裡面可以讓你設定需要載入哪些必要的 Polyfills 函式庫,好讓你的網站也可以支援舊版瀏覽器執行。
目前 Angular 支援的 Polyfills APIs 最低可支援到 IE9 瀏覽器!
Angular CLI 內建的 Polyfills 定義一直在進化,幾乎每一個大版都會有些更貼心的設計出現,我們來看看最近幾版的變化與差異。
關於 Angular CLI 6 的 polyfills.ts 檔案
如果你用 Angular CLI 6 建立的 Angular 專案,其預設的 src/polyfills.ts
檔案內容如下。註解中詳細說明了使用的時機點,想支援哪個瀏覽器就要匯入必要的 Polyfill 模組,有些模組需要額外透過 npm 進行安裝,記得要安裝之後才能使用。
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
關於 Angular CLI 7 的 polyfills.ts 檔案
我在 Angular Taiwan 2018 技術大會的【Angular 7 全新功能探索】演講中曾經分享過關於 Polyfills 的改進。但我們先來看一下這個版本預設的 src/polyfills.ts
檔案內容:
import 'zone.js/dist/zone';
大部分內容都是一樣的,除了有些註解說明有調整外,主要是有一行不見了!
import 'core-js/es7/reflect';
原本在 Angular CLI 6 之前有一個 reflect-metadata 套件預設被載入,但此套件只有執行在 JIT 模式的時候才需要,一般來說我們都會用 AOT 模式建置專案,因此這個檔案經常需要「手動」註解掉,但很多人不知道這個細節,導致這個檔案一直被額外載入,浪費了 20.7KB
左右的下載流量。
從 Angular CLI 7.0 開始,這項設定已經被拿掉,改由 ng build
的過程透過 webpack 全自動判斷是否加入,相當貼心的設計!
關於 Angular CLI 7.3 的 polyfills.ts 檔案
從 Angular CLI 7.3 開始,其預設的 src/polyfills.ts
檔案內容如下:
import 'zone.js/dist/zone';
這個版本就更激進了,直接拿掉以下內容:
不過各位不用擔心,並不是說從此不支援 IE9 ~ IE11,而是有新的設計出現,叫做 Conditional ES5 Browser Polyfill Loading!
條件式 ES5 瀏覽器相容套件載入機制
全新的 Angular CLI 7.3 在 angular.json
設定檔中增加了一條選項設定 "es5BrowserSupport": true
:
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/demo1",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": [],
"es5BrowserSupport": true
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
如果從舊版升級上來,記得要手動調整 src/polyfills.ts
與 angular.json
的選項設定!(schema.json)
這個 "es5BrowserSupport"
選項如果設定為 true
的話,當你使用 ng build
或 ng build --prod
的時候就會自動自動幫你打包上述這幾個 Polyfills 到一個獨立的 JS 檔案中,檔名為 es2015-polyfills.*****.js
但真正的重點在於這個檔案的載入方式,請看一下 ng build --prod
完後後建置出來的 index.html
檔案內容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Demo1</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" href="styles.3ff695c00d717f2d2a11.css" />
</head>
<body>
<app-root></app-root>
<script
type="text/javascript"
src="runtime.b57bf819d5bdce77f1c7.js"
></script>
<script
type="text/javascript"
src="es2015-polyfills.41976a8133a2445ac0d9.js"
nomodule
></script>
<script
type="text/javascript"
src="polyfills.fb4ac03bdf7e23477d5b.js"
></script>
<script type="text/javascript" src="main.e42ec0deb336589a6946.js"></script>
</body>
</html>
看到亮點了嗎?
這裡的 es2015-polyfills.*****.js
檔案被載入時,被加上了 nomodule
屬性。這個屬性只有在支援 ES2015 的瀏覽器才認得,這些現代化的瀏覽器只要看見 <script>
標籤使用了 nomodule
屬性,就會自動忽略這個 JS 檔案載入,並不是載入後不執行,而是連載入動作都不會發生,大幅節省頻寬!(約 56K
左右)
但是 IE 或其他舊版瀏覽器,並不認得 nomodule
屬性,所以檔案就還是會載入並執行。如此一來,就徹底解決了跨瀏覽器相容的問題,是不是相當漂亮!
Safari 10.1 不支援 nomodule 的問題
目前已知支援 ES2015 模組化技術的瀏覽器版本如下:
- Safari 10.1+
- Chrome 61+
- Firefox 60+
- Edge 16+
不過 Safari 10.1 卻不支援 nomodule
屬性,解決方法可以參考 Safari 10.1 nomodule support 這個 Polyfills 來解決。這份 Polyfills 有詳細註解說明方式,請看仔細再加入到你的現有專案中!
相關連結