新聞中心
Angular Universal:Angular 統(tǒng)一平臺簡介
本指南講的是Angular Universal(統(tǒng)一平臺),一項在服務端運行 Angular 應用的技術。

成都創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設、高性價比五華網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式五華網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設找我們,業(yè)務覆蓋五華地區(qū)。費用合理售后完善,10多年實體公司更值得信賴。
標準的 Angular 應用會運行在瀏覽器中,它會在 DOM 中渲染頁面,以響應用戶的操作。而Angular Universal 會在服務端運行,生成一些靜態(tài)的應用頁面,稍后再通過客戶端進行啟動。這意味著該應用的渲染通常會更快,讓用戶可以在應用變得完全可交互之前,先查看應用的布局。
要了解 SSR 的其它技術和概念的詳細信息,請參閱這篇文章。
可以使用 ?Angular CLI? 來輕松為應用做好服務端渲染的準備。CLI 的 ?@nguniversal/express-engine? 模板會執(zhí)行如下必要步驟。
Angular Universal 需要活躍 LTS 或 維護中 LTS版本的 Node.js。參見 package.json 文件中的 ?
engines?屬性,以了解當前支持的版本。
注意:
下載已完成的范例代碼,并將其運行在一個 Node.js? Express 服務器中。
Universal 教程
這次演練的基礎是“英雄之旅”教程。
在這個例子中,Angular CLI 使用 預先(AoT)編譯器編譯并打包了該應用的 Universal 版本。Node.js Express Web 服務器則會根據(jù)客戶端的請求,利用 Universal 編譯 HTML 頁面。
要創(chuàng)建服務端應用模塊 ?app.server.module.ts?,請運行以下 CLI 命令。
ng add @nguniversal/express-engine該命令會創(chuàng)建如下文件夾結構。
標有 ?*? 的文件都是新增的,不在原始的教程范例中。
Universal 實戰(zhàn)
要使用 Universal 在本地系統(tǒng)中渲染你的應用,請使用如下命令。
npm run dev:ssr打開瀏覽器,導航到 ?http://localhost:4200?。你會看到熟悉的“英雄之旅”儀表盤頁面。
通過 ?routerLinks ?導航時能正常工作,因為它們使用的是內置的鏈接元素(??)。你可以從儀表盤進入 英雄列表頁面,然后返回。你可以點擊儀表盤頁面上的一個英雄來顯示他的詳情頁面。
如果你限制下網(wǎng)速(稍后會講操作步驟),讓客戶端腳本下載時間變長,你會注意到:
- 你無法添加或刪除英雄
- 儀表盤頁面上的搜索框會被忽略
- “詳情”頁面上的后退和保存按鈕不起作用
不支持除了點擊 routerLink 以外的任何用戶事件。你必須等待完整的客戶端應用啟動并運行,或者使用 preboot 之類的庫來緩沖這些事件,這樣你就可以在客戶端腳本加載完畢后重放這些事件。
在開發(fā)機器上,從服務端渲染的應用過渡到客戶端應用的過程會很快,但是你還是應該在實際場景中測試一下你的應用。
你可以通過模擬速度較慢的網(wǎng)絡來更清晰地看到這種轉換,如下所示:
- 打開 Chrome 開發(fā)者工具,進入 Network 標簽頁。
- 找一下菜單欄最右側的 Network Throttling 下拉菜單。
- 嘗試一下 “3G” 的速度吧。
服務端渲染的應用仍然可以快速啟動,但完整的客戶端應用可能需要幾秒鐘才能加載完。
為何需要服務端渲染?
有三個主要的理由來為你的應用創(chuàng)建一個 Universal 版本。
- 通過搜索引擎優(yōu)化(SEO)來幫助網(wǎng)絡爬蟲。
- 提升手機和低功耗設備上的性能
- 迅速顯示出第一個支持首次內容繪制(FCP)的頁面
幫助網(wǎng)絡爬蟲(SEO)
Google、Bing、Facebook、Twitter 和其它社交媒體網(wǎng)站都依賴網(wǎng)絡爬蟲去索引你的應用內容,并且讓它的內容可以通過網(wǎng)絡搜索到。 這些網(wǎng)絡爬蟲可能不會像人類那樣導航到你的具有高度交互性的 Angular 應用,并為其建立索引。
Angular Universal 可以為你生成應用的靜態(tài)版本,它易搜索、可鏈接,瀏覽時也不必借助 JavaScript。它也讓站點可以被預覽,因為每個 URL 返回的都是一個完全渲染好的頁面。
提升手機和低功耗設備上的性能
有些設備不支持 JavaScript 或 JavaScript 執(zhí)行得很差,導致用戶體驗不可接受。對于這些情況,你可能會需要該應用的服務端渲染的、無 JavaScript 的版本。雖然有一些限制,不過這個版本可能是那些完全沒辦法使用該應用的人的唯一選擇。
快速顯示第一頁
快速顯示第一頁對于吸引用戶是至關重要的。加載速度更快的頁面效果更好,即使其差異只有 100 毫秒也是如此(https://web.dev/shopping-for-speed-on-ebay/)。你的應用要啟動得更快一點,以便在用戶決定做別的事情之前吸引他們的注意力。
使用 Angular Universal,你可以為應用生成“著陸頁”,它們看起來就和完整的應用一樣。這些著陸頁是純 HTML,并且即使 JavaScript 被禁用了也能顯示。這些頁面不會處理瀏覽器事件,不過它們可以用 ?[routerLink]??(guide/router-reference#router-link)? 在這個網(wǎng)站中導航。
在實踐中,你可能要使用一個著陸頁的靜態(tài)版本來保持用戶的注意力。同時,你也會在幕后加載完整的 Angular 應用。用戶會覺得著陸頁幾乎是立即出現(xiàn)的,而當完整的應用加載完之后,又可以獲得完整的交互體驗。
Universal Web 服務器
Universal Web 服務器使用 Universal 模板引擎渲染出的靜態(tài) HTML 來響應對應用頁面的請求。 服務器接收并響應來自客戶端(通常是瀏覽器)的 HTTP 請求,并回復靜態(tài)文件,如腳本、CSS 和圖片。 它可以直接響應數(shù)據(jù)請求,也可以作為獨立數(shù)據(jù)服務器的代理進行響應。
這個例子中的范例 Web 服務器是基于常見的 Express 框架的。
注意:
任何一種 Web 服務器技術都可以作為 Universal 應用的服務器,只要它能調用 Universal 的 ?
renderModule()? 函數(shù)。 這里所討論的這些原則和決策點也適用于任何 Web 服務器技術。
Universal 應用使用 ?platform-server? 包(而不是 ?platform-browser?),它提供了 DOM 的服務端實現(xiàn)、?XMLHttpRequest ?以及其它不依賴瀏覽器的底層特性。
服務器(這個例子中使用的是 Node.js Express 服務器)會把客戶端對應用頁面的請求傳給 NgUniversal 的 ?ngExpressEngine?。在內部實現(xiàn)上,它會調用 Universal 的 ?renderModule()? 函數(shù),它還提供了緩存等有用的工具函數(shù)。
?renderModule()? 函數(shù)接受一個模板 HTML 頁面(通常是 ?index.html?)、一個包含組件的 Angular 模塊和一個用于決定該顯示哪些組件的路由作為輸入。 該路由從客戶端的請求中傳給服務器。
每次請求都會給出所請求路由的一個適當?shù)囊晥D。?renderModule()? 在模板中的 ?? 標記中渲染出這個視圖,并為客戶端創(chuàng)建一個完成的 HTML 頁面。
最后,服務器就會把渲染好的頁面返回給客戶端。
使用瀏覽器 API
由于 Universal 應用并沒有運行在瀏覽器中,因此該服務器上可能會缺少瀏覽器的某些 API 和其它能力。
比如,服務端應用不能引用瀏覽器獨有的全局對象,比如 ?window?、?document?、?navigator ?或 ?location?。
Angular 提供了一些這些對象的可注入的抽象層,比如 ?Location ?或 ?DOCUMENT?,它可以作為你所調用的 API 的等效替身。如果 Angular 沒有提供它,你也可以寫一個自己的抽象層,當在瀏覽器中運行時,就把它委托給瀏覽器 API,當它在服務器中運行時,就提供一個符合要求的代用實現(xiàn)(也叫墊片 - shimming)。
同樣,由于沒有鼠標或鍵盤事件,因此 Universal 應用也不能依賴于用戶點擊某個按鈕來顯示某個組件。Universal 應用必須僅僅根據(jù)客戶端過來的請求決定要渲染的內容。把該應用做成可路由的,就是一種好方案。
Universal 模板引擎
?server.ts? 文件中最重要的部分是 ?ngExpressEngine()? 函數(shù)。
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));?ngExpressEngine()? 是對 Universal 的 ?renderModule()? 函數(shù)的封裝。它會把客戶端請求轉換成服務端渲染的 HTML 頁面。它接受一個具有下列屬性的對象:
|
屬性 |
詳情 |
|---|---|
bootstrap |
在服務器上渲染時用于引導應用程序的根 |
extraProviders |
這是可選的,可以讓你指定僅在服務器渲染應用程序時才適用的依賴提供者。當你的應用需要某些只能由當前運行的服務器實例確定的信息時,可以執(zhí)行此操作。 |
?ngExpressEngine()? 函數(shù)返回了一個會解析成渲染好的頁面的承諾(Promise)。接下來你的引擎要決定拿這個頁面做點什么。在這個引擎的 ?Promise ?回調函數(shù)中,把渲染好的頁面返回給了 Web 服務器,然后服務器通過 HTTP 響應把它轉發(fā)給了客戶端。
注意:
這個包裝器幫助隱藏了 ?
renderModule()? 的復雜性。 在 Universal 代碼庫中還有更多針對其它后端技術的包裝器。
過濾請求的 URL
注意:
當使用 NgUniversal Express 原理圖時,將自動處理稍后描述的基本行為。當你要嘗試理解其底層行為或在不使用原理圖的情況下自行實現(xiàn)它時,這一節(jié)會很有用。
Web 服務器必須把對應用頁面的請求和其它類型的請求區(qū)分開。
這可不像攔截對根路徑 ?/? 的請求那么簡單。瀏覽器可以請求應用中的任何一個路由地址,比如 ?/dashboard?、?/heroes? 或 ?/detail:12?。事實上,如果應用只會通過服務器渲染,那么應用中點擊的任何一個鏈接都會發(fā)到服務器,就像導航時的地址會發(fā)到路由器一樣。
幸運的是,應用的路由具有一些共同特征:它們的 URL 一般不帶文件擴展名。(數(shù)據(jù)請求也可能缺少擴展名,但是它們很容易識別出來,因為它們總是以 ?/api? 開頭,所有的靜態(tài)資源的請求都會帶有一個擴展名,比如 ?main.js? 或 ?/node_modules/zone.js/dist/zone.js?)。
由于使用了路由,所以我們可以輕松的識別出這三種類型的請求,并分別處理它們。
|
路由請求類型 |
詳情 |
|---|---|
|
數(shù)據(jù)請求 |
請求的 URL 用 |
|
應用導航 |
請求的 URL 不帶擴展名。 |
|
靜態(tài)資產 |
所有其它請求。 |
Node.js Express 服務器是一系列中間件構成的管道,它會挨個對 URL 請求進行過濾和處理。你可以調用 ?app.get()? 來配置 Express 服務器的管道,就像下面這個數(shù)據(jù)請求一樣。
// TODO: implement data requests securely
server.get('/api/**', (req, res) => {
res.status(404).send('data requests are not yet supported');
});注意:
這個范例服務器不會處理數(shù)據(jù)請求。
本教程的“內存 Web API” 模塊(一個演示及開發(fā)工具)攔截了所有 HTTP 調用,并且模擬了遠端數(shù)據(jù)服務器的行為。在實踐中,你應該移除這個模塊,并且在服務器上注冊你的 Web API 中間件。
下列代碼會過濾出不帶擴展名的 URL,并把它們當做導航請求進行處理。
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});安全的提供靜態(tài)文件
單獨的 ?server.use()? 會處理所有其它 URL,比如對 JavaScript 、圖片和樣式表等靜態(tài)資源的請求。
要保證客戶端只能下載那些允許他們訪問的文件,你應該把所有面向客戶端的資源文件都放在 ?/dist? 目錄下,并且只允許客戶端請求來自 ?/dist? 目錄下的文件。
下列 Node.js Express 代碼會把剩下的所有請求都路由到 ?/dist? 目錄下,如果文件未找到,就會返回 ?404 - NOT FOUND?。
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));在服務端使用絕對 URL 進行 HTTP(數(shù)據(jù))請求
本教程的 ?HeroService ?和 ?HeroSearchService ?都委托 Angular 的 ?HttpClient ?模塊來獲取應用數(shù)據(jù)。這些服務會向 ?api/heroes? 之類的相對 URL 發(fā)送請求。在服務端渲染的應用中,HTTP URL 必須是絕對的(比如,?https://my-server.com/api/heroes?)。這意味著當在服務器上運行時,URL 必須以某種方式轉換為絕對 URL,而在瀏覽器中運行時,它們是相對 URL。
如果你正在使用 ?@nguniversal/*-engine? 包之一(比如 ?@nguniversal/express-engine?),就會自動為幫你做這件事。你無需再做任何事情來讓相對 URL 能在服務器上運行。
如果出于某種原因,你沒有使用 ?@nguniversal/*-engine? 包,你可能需要親自處理它。
建議的解決方案是將完整的請求 URL 傳給 ?renderModule()? 或 ?renderModuleFactory()? 的 ?options ?參數(shù)(具體取決于你在服務器上渲染 ?AppServerModule ?的目的)。此選項的侵入性最小,因為它不需要對應用進行任何更改。這里的“請求 URL” 是指當應用在服務器上渲染時的地址。比如,如果客戶端請求了 ?https://my-server.com/dashboard? 并且要在服務器上渲染該應用以響應該請求,那么 ?options.url? 應設置為 ?https://my-server.com/dashboard?。
現(xiàn)在,作為在服務端渲染應用的一部分,每次發(fā)送 HTTP 請求時,Angular 都可以使用這里提供的 ?options.url? 正確地將請求 URL 解析為絕對 URL。
實用腳本
| 腳本 | 詳情 |
?npm run dev:ssr? |
此命令類似于 ?ng serve?,它在開發(fā)期間提供實時重新加載,但使用服務器端渲染。該應用程序以監(jiān)視模式運行并在每次更改后刷新瀏覽器。這個命令要比實際的 ?ng serve? 命令慢。 |
? ng build && ng run app-name:server? |
此命令會在生產模式下構建服務器腳本和應用程序。當你要構建用于部署的項目時,請使用此命令。 |
? npm run serve:ssr? |
此命令啟動服務器腳本,用于通過服務器端渲染在本地為應用程序提供服務。它使用由 ? |
? npm run prerender? |
此腳本可用于預先渲染應用程序的頁面。 |
網(wǎng)站標題:創(chuàng)新互聯(lián)Angular教程:Angular服務端渲染
當前路徑:http://fisionsoft.com.cn/article/dhccede.html


咨詢
建站咨詢
