新聞中心
Angular Universal:Angular 統(tǒng)一平臺(tái)簡(jiǎn)介
本指南講的是Angular Universal(統(tǒng)一平臺(tái)),一項(xiàng)在服務(wù)端運(yùn)行 Angular 應(yīng)用的技術(shù)。

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


咨詢
建站咨詢
