如何使用 Angular 伺服器端渲染的 Transfer State Service

注销發表於2022-05-19

假設我們使用 Angular Universal 開發一個伺服器端渲染的 Angular 應用,這個應用會消費一個第三方的 Restful API.

上述場景分為下列六個步驟:

  1. 使用者向部署了 Angular 伺服器端應用的 Node.js 伺服器發起頁面請求
  2. Node.js 呼叫第三方 Restful API,
  3. 第三方 Restful API 返回結果,這個結果被用於渲染最後的頁面
  4. 伺服器端渲染的頁面,返回給瀏覽器
  5. Angular 在瀏覽器中引導,並再次呼叫 Restful API
  6. 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);
    }
  }

相關文章