最近推出的 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 進行安裝,記得要安裝之後才能使用。
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
*/
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
/*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*/
// (window as any).__Zone_enable_cross_context_check = true;
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
關於 Angular CLI 7 的 polyfills.ts 檔案
我在 Angular Taiwan 2018 技術大會的【Angular 7 全新功能探索】演講中曾經分享過關於 Polyfills 的改進。但我們先來看一下這個版本預設的 src/polyfills.ts
檔案內容:
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills.
* This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot
*/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
大部分內容都是一樣的,除了有些註解說明有調整外,主要是有一行不見了!
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
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
檔案內容如下:
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
這個版本就更激進了,直接拿掉以下內容:
/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills.
* This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot
*/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
不過各位不用擔心,並不是說從此不支援 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 有詳細註解說明方式,請看仔細再加入到你的現有專案中!
相關連結