[從 0 開始的 Angular 生活]No.52 使用 Angular Universal 讓網站 SEO 提升

前言

使用 Angular 建立的網站是屬於 SPA (Single-Page Application) 單頁應用,是一種網路應用程式或網站的模型,它通過動態重寫目前頁面來與用戶互動,而非傳統的從伺服器重新載入整個新頁面。

雖然現在 Google 的爬蟲已經可以看得懂 SPA 架構的網站,但其他的搜尋引擎不見得看得懂,因此使用 SSR (Server Side Render) 技術來輔助網站的 SEO 還是必要的。

關於更詳細的名詞解釋可以參考 前後端分離與 SPA - Huli ,本文不會有太多著墨。

Angular Universal

其他前端主流框架如 React 或 Vue 對於 SPA 不利於 SEO 都有相關的解決辦法,如:

  • React 可以使用 Next
  • Vue 可以使用 Nuxt

而 Angular 的解決方案就是使用 Angular Universal了。

在早期 Angular 要使用 SSR 技術似乎是相當麻煩的要修改很多地方,但現在卻可以使用少少的幾行指令就達成!而且幾乎是手把手照著 Angular 官網的步驟做,是不是相當友善呢~

建立新的空白專案

在終端機內輸入 ng new [projectName] 就可以建立起一個 Angular 的專案,這個示範中建立了 SSRDemo 專案。

運行 SSRDemo

加入 @nguniversal/express-engine 到專案中

在終端機輸入以下指令:

1
ng add @nguniversal/express-engine --clientProject SSRDemo

如此一來 Angular CLI 就會幫這個專案產生必要的檔案如: app.server.module.ts

執行指令後的異動

不難看出 Angular CLI 幫我們做了相當多的事情,在早期這可是需要自己手動進行的而且不太容易。

運行 Universal Web 伺服器

所有的準備工作都已經就緒,可以在終端機輸入以下指令啟動伺服器:

1
npm run build:ssr && npm run serve:ssr

會得到乍看之下與尚未使用 SSR 技術前一樣的結果:

但實際上原始的程式碼內已經大不相同了!
使用 SSR

不使用 SSR

不難看出有使用 SSR 技術的原始碼內多了不少內容,而這些內容就是要給搜尋引擎爬蟲看的。

在 HTTP 中使用絕對位置

在 Universal 應用中 HTTP 的 URL 必須是絕對位置,只有這樣 Universal 的 Web 伺服器才能處理那些請求。

這意味著當執行在伺服器端時,要使用絕對 URL 發起請求,而在瀏覽器中,則使用相對 URL。

在官網的 Angular 教學 (英雄範例) 中的服務都把請求送到了相對的 URL ,所以為了使其正常運作,需要額外建立 HttpInterceptor ,令其使用絕對 URL 發起請求。

建立 universal-interceptor.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import {Injectable, Inject, Optional} from '@angular/core';
import {HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders} from '@angular/common/http';
import {Request} from 'express';
import {REQUEST} from '@nguniversal/express-engine/tokens';

@Injectable()
export class UniversalInterceptor implements HttpInterceptor {

constructor(@Optional() @Inject(REQUEST) protected request: Request) {}

intercept(req: HttpRequest<any>, next: HttpHandler) {
let serverReq: HttpRequest<any> = req;
if (this.request) {
let newUrl = `${this.request.protocol}://${this.request.get('host')}`;
if (!req.url.startsWith('/')) {
newUrl += '/';
}
newUrl += req.url;
serverReq = req.clone({url: newUrl});
}
return next.handle(serverReq);
}
}

調整 app.server.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {UniversalInterceptor} from './universal-interceptor';

@NgModule({
...
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: UniversalInterceptor,
multi: true
}],
})
export class AppServerModule {}

現在當伺服器發起每個 HTTP 請求時,該攔截器都會被觸發,並把請求的 URL 替換為由 Express 的 Request 物件給出的絕對位置。

參考文件 & 程式碼

一開始還沒實作時看了蠻多篇文章的,但距今都有一定的時日了,在前端技術迭代的如此迅速的情況下,一篇幾年前的文章參考價值就不那麼高了,但對於了解整個脈絡還是很有幫助的,因此還是列出大致上看過那些文章:

程式碼

小結

結束了本篇的學習後,下一篇文章我們將試著將這份專案正式部屬到 Heroku 讓練習更加完整。

0%