天天看點

Angular Universal:Angular 統一平台簡介

Angular Universal

本文介紹 Angular Universal(統一平台),一項在服務端運作 Angular 應用的技術,即伺服器端渲染。

如下圖 package.json 裡定義的依賴 @nguniversal/express-engine 所示:

标準的 Angular 應用會運作在浏覽器中,它會在 DOM 中渲染頁面,以響應使用者的操作。 而Angular Universal 會在服務端運作,生成一些靜态的應用頁面,稍後再通過用戶端進行啟動。 這意味着該應用的渲染通常會更快,讓使用者可以在應用變得完全可互動之前,先檢視應用的布局。

伺服器端渲染傳回的 HTML 源代碼,雖然沒有加載對應的 .js 檔案,無法響應使用者輸入,但是可以給使用者一個直覺完整的頁面布局。

Node.js Express Web 伺服器則會根據用戶端的請求,利用 Universal 編譯 HTML 頁面。

要建立服務端應用子產品 app.server.module.ts,請運作以下 CLI 指令:

ng add @nguniversal/express-engine

該指令會自動生成如下綠色高亮所示的檔案:

要使用 Universal 在本地系統中渲染你的應用,請使用如下指令:

npm run dev:ssr

這個 serve-ssr 定義在 Angular.json 裡:

而 server target 定義如下:

通過 routerLinks 導航時能正常工作,因為它們使用的是原生的連結标簽().

不支援除了點選 routerLink 以外的任何使用者事件。你必須等待完整的用戶端應用啟動并運作,或者使用 preboot 之類的庫來緩沖這些事件,這樣你就可以在用戶端腳本加載完畢後重放這些事件。

Angular Universal 可以為你生成應用的靜态版本,它易搜尋、可連結,浏覽時也不必借助 JavaScript。 它也讓站點可以被預覽,因為每個 URL 傳回的都是一個完全渲染好的頁面。

使用 Angular Universal,你可以為應用生成“着陸頁”,它們看起來就和完整的應用一樣。 這些着陸頁是純 HTML,并且即使 JavaScript 被禁用了也能顯示。 這些頁面不會處理浏覽器事件,不過它們可以用 routerLink 在這個網站中導航。

在實踐中,你可能要使用一個着陸頁的靜态版本來保持使用者的注意力。 同時,你也會在幕後加載完整的 Angular 應用。 使用者會覺得着陸頁幾乎是立即出現的,而當完整的應用加載完之後,又可以獲得完整的互動體驗。

Universal Web 伺服器使用 Universal 模闆引擎渲染出的靜态 HTML 來響應對應用頁面的請求。 伺服器接收并響應來自用戶端(通常是浏覽器)的 HTTP 請求,并回複靜态檔案,如腳本、CSS 和圖檔。 它可以直接響應資料請求,也可以作為獨立資料伺服器的代理進行響應。

任何一種 Web 伺服器技術都可以作為 Universal 應用的伺服器,隻要它能調用 Universal 的 renderModule() 函數。 這裡所讨論的這些原則和決策點也适用于任何 Web 伺服器技術。

Universal 應用使用 platform-server 包(而不是 platform-browser),它提供了 DOM 的服務端實作、XMLHttpRequest 以及其它不依賴浏覽器的底層特性。

伺服器(這個例子中使用的是 Node.js Express 伺服器)會把用戶端對應用頁面的請求傳給 NgUniversal 的 ngExpressEngine。在内部實作上,它會調用 Universal 的 renderModule() 函數,它還提供了緩存等有用的工具函數。

關于具體的調試步驟,參考我這些文章:

SAP Spartacus 伺服器端渲染單步調試步驟之一:應用程式準備工作

SAP Spartacus 伺服器端渲染單步調試步驟之二:在伺服器端執行應用程式 Angular 代碼

使用浏覽器 API

由于 Universal 應用并沒有運作在浏覽器中,是以該伺服器上可能會缺少浏覽器的某些 API 和其它能力。

比如,服務端應用不能引用浏覽器獨有的全局對象,比如 window、document、navigator 或 location。

Angular 提供了一些這些對象的可注入的抽象層,比如 Location 或 DOCUMENT,它可以作為你所調用的 API 的等效替身。 如果 Angular 沒有提供它,你也可以寫一個自己的抽象層,當在浏覽器中運作時,就把它委托給浏覽器 API,當它在伺服器中運作時,就提供一個符合要求的代用實作(也叫墊片 - shimming)。

同樣,由于沒有滑鼠或鍵盤事件,是以 Universal 應用也不能依賴于使用者點選某個按鈕來顯示每個元件。 Universal 應用必須僅僅根據用戶端過來的請求決定要渲染的内容。 把該應用做成可路由的,就是一種好方案。

Universal 模闆引擎

server.ts 的核心邏輯如下代碼所示:

Angular Universal:Angular 統一平台簡介

ngExpressEngine() 是對 Universal 的 renderModule() 函數的封裝。它會把用戶端請求轉換成服務端渲染的 HTML 頁面。它接受一個具有下列屬性的對象,類型為 NgSetupOptions:

Angular Universal:Angular 統一平台簡介

bootstrap:在伺服器上渲染時用于引導應用程式的根 NgModule 或 NgModule 工廠。對于 SAP Commerce Cloud 應用,它是 AppServerModule。它是 Universal 服務端渲染器和 Angular 應用之間的橋梁。

Angular Universal:Angular 統一平台簡介

ngExpressEngine() 函數傳回了一個會解析成渲染好的頁面的承諾(Promise)。 接下來你的引擎要決定拿這個頁面做點什麼。 在這個引擎的 Promise 回調函數中,把渲染好的頁面傳回給了 Web 伺服器,然後伺服器通過 HTTP 響應把它轉發給了用戶端。

過濾請求的 URL

Web 伺服器必須把對應用頁面的請求和其它類型的請求區分開。

這可不像攔截對根路徑 / 的請求那麼簡單。 浏覽器可以請求應用中的任何一個路由位址,比如 /dashboard、/heroes 或 /detail:12。 事實上,如果應用隻會通過伺服器渲染,那麼應用中點選的任何一個連結都會發到伺服器,就像導航時的位址會發到路由器一樣。

幸運的是,應用的路由具有一些共同特征:它們的 URL 一般不帶檔案擴充名。 (資料請求也可能缺少擴充名,但是它們很容易識别出來,因為它們總是以 /api 開頭,所有的靜态資源的請求都會帶有一個擴充名,比如 main.js 或 /node_modules/zone.js/dist/zone.js)。

由于使用了路由,是以我們可以輕松的識别出這三種類型的請求,并分别處理它們。

資料請求:請求的 URL 用 /api 開頭

應用導航:請求的 URL 不帶擴充名

靜态資源:所有其它請求。

Node.js Express 伺服器是一系列中間件構成的管道,它會挨個對 URL 請求進行過濾和處理。 你可以調用 app.get() 來配置 Express 伺服器的管道,就像下面這個資料請求一樣:

Angular Universal:Angular 統一平台簡介

繼續閱讀