假設我們使用 Angular Universal 開發一個伺服器端渲染的 Angular 應用,這個應用會消費一個第三方的 Restful API.
上述場景分為下列六個步驟:
- 使用者向部署了 Angular 伺服器端應用的 Node.js 伺服器發起頁面請求
- Node.js 呼叫第三方 Restful API,
- 第三方 Restful API 返回結果,這個結果被用於渲染最後的頁面
- 伺服器端渲染的頁面,返回給瀏覽器
- Angular 在瀏覽器中引導,並再次呼叫 Restful API
- Restful API 返回給瀏覽器,Angular 客戶端應用重新將資料渲染到檢視中。
我們可以透過建立 TransferState 服務來提高應用程式的效率,該服務是在 Node.js 伺服器和瀏覽器中呈現的應用程式之間交換的一個鍵值登錄檔。
我們將透過一個 HTTP_INTERCEPTOR 機制來使用它,該機制將駐留在 HttpClient 服務中,並將操縱請求和響應。
建立一個新的 class,實現 HttpInterceptor 介面定義的 intercept 方法:
@Injectable({
providedIn: 'root'
})
export class HttpInterceptorService implements HttpInterceptor
public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
每當對 HttpClient 服務執行任何 API 呼叫時,都會呼叫此方法。
為了簡單起見,我們僅針對 GET 方法啟用 TransferState:
if (request.method !== 'GET') {
return next.handle(request);
}
我們根據 GET 請求的 URL 生成一個金鑰。 我們將使用鍵值對來儲存或檢索請求響應,具體取決於請求是在伺服器端還是瀏覽器端處理:
const key: StateKey<string> = makeStateKey<string>(request.url);
為了區分伺服器和瀏覽器執行環境,我們使用 @angular/common
庫中的 isPlatformServer 方法以及 PLATFORM_ID 注入令牌:
if (isPlatformServer(this.platformId)) {
//serverSide
} else {
//browserSide
}
當伺服器端渲染時,我們將 API 結果寫入 Transfer State 登錄檔中:
if (isPlatformServer(this.platformId)) {
return next.handle(request).pipe(tap((event) => {
this.transferState.set(key, (<HttpResponse<any>> event).body);
}));
在瀏覽器端程式碼中,我們要檢查給定 HTTP 請求的響應是否已經駐留在 Transfer State 登錄檔中。 如果存在,我們直接從登錄檔中取出值,並清除登錄檔,以便將來的呼叫可以儲存新資料,並將響應返回給呼叫者。
當且僅當登錄檔中不存在給定的鍵,我們才在客戶端環境下執行 HTTP 呼叫。
else {
const storedResponse = this.transferState.get<any>(key, null);
if (storedResponse) {
const response = new HttpResponse({body: storedResponse, status: 200});
this.transferState.remove(key);
return of(response);
} else {
return next.handle(request);
}
}