翻譯:瘋狂的技術宅原文:https://jaxenter.com/whats-ne...html
未經容許嚴禁轉載前端
Angular 8 終於來了,包括 Ivy 的預覽、service worker 支持,差別化加載以及一些錦上添花的東西。 Manfred Steyer 解釋了最新 Angular 版本中最重要的變化。
Angular 8 剛剛發佈!git
徹底按照計劃,沒有任何意外:框架和 CLI 的更新能夠經過 ng update
完成,其新功能是一個受歡迎的補充,符合「演化而不是革命」的座右銘。程序員
在本文中,我將介紹 Angular 8 和 Angular CLI 8 的最重要的新功能。我在文中的例子能夠在 GitHub 上找到。github
Ivy 是 Angular 世界下一個望眼欲穿的大新聞,它是新的 Angular 編譯器,也是新的渲染管道。Ivy 有可能產生至關小 bundle,它使漸進式編譯更容易,也是 Angular 領域將來創新的基礎。面試
因爲 Angular 大量的底層部分已經爲此進行了更改,所以 Angular 團隊特別注意與之前的 Angular 版本的兼容性:在切換到 Ivy 以後,現有的程序應該可以像之前同樣工做。在一切正常的前提下,可以獲得明顯更小的 bundles 應該就足夠了。這並不是是他們大發善心,而是由於 Google 有 600 多個以 Angular 爲基礎的應用程序 —— 儘管是謠傳,但實際數字要高得多。算法
在 Angular 8 中 Ivy 的預覽版如今可供測試。此版本的目標是得到早期反饋。所以,Angular 團隊建議不要把 Ivy 用於生產環境,而是繼續使用經典視圖引擎(圖1)。json
使用與不使用 Ivy 時的 hello world 程序的 Bundle 大小(來源:由Brad Green和Igor Minar撰寫的 ngconf 2019 主題演講)segmentfault
感謝差別加載(以下所示),bundle 大小已經能夠當即獲得優化。瀏覽器
正如 Google Angular 團隊背後的技術總監 Brad Green 在 ngconf 2019 中提到的那樣,Ivy 將在兼容模式下結合差別加載,顯着改善 bundle 的尺寸。尋求刺激的人能夠嘗試一下將來的 Ivy API。該模式下有很是大的優化潛力。目前這些 API 仍然被標記爲私有。你能夠經過查看它的類和函數來進行判斷:它們以特殊字符 ɵ
開頭。
若是你想嘗試 Ivy,能夠經過 enable-ivy
開關生成一個新項目:
ng new ivy-project --enable-ivy
這樣作的結果是 CLI 會在 tsconfig.app.json
中存儲如下配置條目:
"angularCompilerOptions": { "enableIvy": true }
在更新到 Angular 8 以後,也能夠手動添加此條目,以便用 Ivy 測試現有程序。
要在調試模式下運行程序,建議使用 AOT:
ng serve --aot
此外,值得一提的是經過 ng build
建立的程序的大小。等到 Angular 9 發佈時 Ivy 最終應該會默認激活。在此以前,Angular 團隊計劃採起進一步措施以確保與舊版本的兼容性。
根據定義,JavaScript 是單線程的。所以,對於數據調用等較大任務異步處理是很常見的。不用說,這對計算密集型沒有幫助。特別是那些普遍的 JavaScript 解決方案變得愈來愈廣泛,這就是爲何如今幾乎全部的瀏覽器都支持支持 Web worker。它們是瀏覽器在本身的線程中運行的腳本。經過發送消息與瀏覽器選項卡中的線程進行通訊。
雖然 Web worker 自己與 Angular 無關,但在構建過程當中必須考慮它們。目標是爲每一個 Web worker 提供一個 bundle 包。此任務由新的 Angular CLI 完成。
爲了說明這個新功能,我將經過實現所謂的 「n 皇后問題」的 JavaScript 進行說明。這個想法是在棋盤上每行放一個皇后,而不能相互公雞。這意味着在同一行、列或對角線中不能有其餘皇后。
n 皇后問題的一種解決方案
計算棋盤上全部可能的解決方案的算法被認爲是計算密集型的。雖然對有 8 行和 8 列的常規棋盤的計算至關快,可是普通計算機從 12×12 格開始就達到了其極限。當前最高記錄是解決具備 27 x 27 格的解決方案。俄羅斯的超級計算機完成了此任務。
爲了將相似這樣的計算甩給後臺,咱們必須首先用 Angular CLI 建立 一個Web worker:
ng generate worker n-queens
此語句不只爲 worker 建立文件,還爲構建過程和現有文件中的條目建立配置文件。若是同一文件夾包含具備公共文件擴展名 .component.ts
的同名組件,則 CLI 甚至會使用與 Web worker 通訊的代碼對其進行豐富。
worker自己只包含 message 事件的事件監聽器:
import nQueens from './n-queens'; addEventListener('message', ({ data }) => { const result = nQueens(data.count); postMessage(result, undefined); });
當主線程向 worker 發送消息時會執行該事件。該參數包含從主線程發來的信息。在當前的狀況下,它僅限於屬性 count
,它聲明瞭棋盤大小。在計算函數 nQueens
以後,事件監聽器經過 postMessage
將結果發送回主線程。*所以,瀏覽器在那裏觸發 message
事件。
Worker
類被應用於 using 組件來與此 worker 腳本交互:
const count = parseInt(this.count, 10); const worker = new Worker('../logic/n-queens.worker', { type: 'module' // Worker uses EcmaScript modules }); worker.postMessage({count}); worker.addEventListener('message', (event) => { // tslint:disable-next-line: no-console console.debug('worker result', event.data); // Update chessboard this.processResult(event.data); });
組件經過 postMessage
向 worker 發送帶有所需棋盤大小的消息,從而觸發計算。它經過消息事件接收結果。
最後 CLI 負責將工做腳本正確的轉換和捆綁。由此啓動的 TypeScript 編譯器會經過它們的後綴 .worker.ts
來識別它們,它們在由 ng generate worker
生成的 tsconfig.worker.json
中註冊。爲了確保 CLI 在翻譯和捆綁主程序時再也不考慮這些文件,ng generate worker
將相同的文件模式放在 tsconfig.app.json
的 exclude
部分中。
完整的實現包含在做者的樣本集[1]中。爲了便於說明,能夠在主線程和 Web worker 中解決可用的 n 皇后問題。例如,當你爲 12 x 12 棋盤請求解決方案時,你將看到 UI 在第一種狀況下會被凍結,而 worker 的後臺計算不會下降 UI 的可操做性。
目前將程序編譯成舊 ECMAScript 5 代碼仍然是很常見的,由於「古老的 JavaScript 」在今天仍然在處處運行。這意味着 IE 11 和 Google 搜索引擎後面的網絡爬蟲均可以執行這些代碼。
可是,新的 ECMAScript 2015 及其後續版本更加高效:這些版本容許更緊湊的 bundle 包,瀏覽器也能夠更有效地解釋它們。
從版本 8 開始,CLI 包含一個名爲差別加載的功能。其背後的想法是提供兩組 bundle:一組基於 ECMAScript 5 而且針對較舊的瀏覽器,另外一組基於較新的 ECMAScript 版本,例如 ECMAScript 2015,以此爲現代瀏覽器提供上述優點。
要激活差別加載,你不用作太多的事情:只須要爲 ECMAScript 版本設置一個上限和下限。在 tsconfig.json
中輸入版本上限,以下所示:
"target": "es2015"
另外一方面,下限由瀏覽器列表來定義。根據市場份額等特定標準,它是一個用來標識許多支持的瀏覽器的文件。它們能夠存儲在例如 browserslist
文件中,CLI 在生成新項目時同時會在 projectroot
中建立:
> 0.5% last 2 versions Firefox ESR not dead IE 9-11
以下圖所示,browserslist
指向 ECMAScript 5 瀏覽器,條目爲 IE 9-11。所以,CLI 將下限肯定爲此版本。若是 CLI 收到構建( ng build
)指令,則將對兩個版本進行編譯和 bundling 過程:
構建差別加載
這個過程的缺點顯而易見:構建過程所需的時間加倍。
爲了使不一樣的瀏覽器能夠決定要加載哪一個版本的 bundle 包,他們在 index.html
添加中接受 script
的引用:指向 ECMAScript 5 包的那些引用會添加 nomodule
。這可使支持 ECMAScript 2015 及更新版本的瀏覽器忽略這些引用。另外一方面,ECMAScript 2015+ bundle 包由 CLI 經過 type ="module"
實現。所以舊版瀏覽器將忽略這些腳本標記:
<script src="main-es2015.js" type="module"></script> <script src="main-es5.js" nomodule></script>
與 ng build
相比,其餘全部 CLI 命令僅使用上限。在上圖中所示的這種狀況下,是 ECMAScript 2015。出於效率緣由,會發生這種狀況:特別是在調試和測試期間,開發人員但願儘快看到結果,而不須要等待第二次構建。
自 Angular 出現的第一天起,路由就支持延遲加載。到目前爲止,這是經過識別加載模塊的魔術值來完成的:
{ path: 'lazy', loadChildren: () => './lazy/lazy.module#LayzModule' }
「#」號以前的值表示通向模塊實現的文件的路徑;以後的值表明其中包含的類。這種寫做風格也適用於 Angular 8,可是已經被棄用了,如今支持動態 ECMAScript 導入:
{ path: 'lazy', loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule) }
新的書寫風格中仍然包含文件名做爲魔術值。可是因爲許多IDE支持導入,所以無效值將當即返回錯誤。
ViewChild
和 ContentChild
的使用方式發生了重大變化,但遺憾的是,過去並不老是表現出一致的行爲。雖然它們在早期版本中被用於組件請求不在結構指令內的元素,如 ngIf 或 ngFor,但查詢結果已在 ngOnInit 中可用。不然,程序代碼或過早的能夠在 ngAfterViewInit(或 ngAfterContentInit for ContentChild )中訪問它。對於之後因數據綁定而僅加載到 DOM 中的元素,程序代碼必須分別插入 ngAfterViewChecked 或 ngAfterContentChecked。
因爲這種行爲十分使人困惑,因此如今組件必須指定什麼時候應該進行解決:
@ViewChild('info', { static: false }) paragraph: ElementRef;
若是 static
的值爲 true
,則 Angular 會在初始化組件時嘗試查找該元素。這隻在不在結構指令中時纔有效。使用 static:false
時,在啓動或刷新視圖後進行解析。
ng update
命令 會自動嘗試在此處輸入正確的值。若是沒法作到這一點,則會在其位置添加帶有 TODO 的註釋。
與相關裝飾器 ViewChildren
和 ContentChildren
的查詢不受此更改的影響。他們老是表現出 static:false
意義上的動態行爲。
到目前爲止,AngularJS 1.x 和 Angular 與 ngUpgrade 的混合操做中存在的一個問題是:兩個框架的路由有時一直在爭奪 URL。這致使了難以理解的反作用。爲了不這種狀況,可使用相同的 Location 服務去訪問兩個版本框架中的 URL 。
爲實現這一目標,Angular 團隊擴展了Angular Location 服務的可能性,從而爲 AngularJS 中的 $location
提供了替代。
出於這個緣由,在 Location 服務中添加了用於監視URL更改的新方法 onUrlChange
以及其餘修改:
export class AppComponent { constructor(loc: Location, pLoc: PlatformLocation) { loc.onUrlChange((url) => console.debug('url change', url)); console.debug('hostname: ', pLoc.hostname); } }
PlatformLocation 服務提供對 URL 各個部分的附加訪問。有關如何使用 $location
替換的詳細描述(用於更好地交織兩個框架)能夠在這裏找到。此外,你如今能夠找到延遲加載 AngularJS 的想法,它基於前面提到的動態 ECMAScript 導入。
Angular團隊再次表達了本身的觀點:遷移到新的 Angular 版本很容易,而且不須要進行大的更改。使得使用 Google 的 SPA 框架更加溫馨。若是舊版瀏覽器不受支持或不支持單獨的 bundle 包,則差別加載會爲進一步優化 bundles 包。 Web worker 支持代表愈來愈多的計算密集型任務開始進入瀏覽器。如今能夠嘗試用 Ivy 邁出第一步。