1. 程式人生 > 其它 >使用 Angular Transfer State 的一個具體例子

使用 Angular Transfer State 的一個具體例子

Using TransferState API in an Angular v5 Universal App

讓我們用一個具體的例子來說明這篇文章。 我們有一個天氣應用程式,在其側邊欄中顯示城市列表。 當您單擊城市名稱時,該應用程式會顯示該城市的當前天氣。

因為我們希望我們的應用程式是可抓取和可索引的,所以我們使它通用:城市頁面在伺服器上呈現,儲存為 HTML 檔案並由 HTTP 伺服器提供服務。 這些頁面將包含瀏覽器應用程式,因此使用者可以在載入第一頁後使用 Angular 的強大功能繼續在應用程式中導航。

您可以按照以下步驟嘗試這個簡單的示例。

使用下列命令將這個例子 clone 到本地:

$ git clone https://github.com/feloy/ng-demo-transfer-state
$ cd ng-demo-transfer-state
$ git checkout initial 

構建程式:

$ npm install
$ ng build -prod
$ ng build -prod -app server --output-hashing=none

為不同的城市建立不同的頁面:

$ node render-page.js /Paris > dist/Paris
$ node render-page.js /London > dist/London
$ node render-page.js /San%20Fransisco > 'dist/San Fransisco'

您現在可以使用首選的 HTTP 伺服器為 dist 目錄提供服務。

現在,如果您直接訪問頁面 http://your-domain/Paris(這是訪問者來自搜尋引擎的典型情況),您可以觀察到頁面閃爍 - 這是因為內容已經存在並且已經下載到本地了,然後瀏覽器應用程式會重新載入並再次顯示。

TransferState to the rescue

Angular v5 中引入的 TransferState API 可以幫助解決這種情況。 它可以將資料從應用程式的伺服器端傳輸到瀏覽器應用程式。
為此,伺服器應用程式將在它生成的 HTML 頁面中新增我們要傳輸的資料。
包含在此生成的 HTML 頁面中的瀏覽器應用程式將能夠讀取此資料。

在這個分支檢視解決方案。

$ git checkout transfer-data

首先在伺服器應用上匯入 ServerTransferStateModule,在瀏覽器應用上匯入 BrowserTransferStateModule:

// src/app/app.server.module.ts
import {ServerTransferStateModule} from '@angular/platform-server';
[...]
@NgModule({
  imports: [
    AppModule,
    ServerModule,
    ServerTransferStateModule
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}
// src/app/app.module.ts
import { BrowserTransferStateModule } from '@angular/platform-browser';
[...]
@NgModule({
  declarations: [
    AppComponent, CityComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-demo-transfer-state-app' }),
    BrowserTransferStateModule,
  [...]

現在,在為元件提供資料的解析器中,我們可以使用 TransferState API:

  • 在伺服器上,我們首先註冊 onSerialize 以提供我們將下載的資料,然後我們從我們的資料提供者那裡獲取資料,這裡是一個 HTTP GET 請求。
  • 在瀏覽器上,我們使用get方法來獲取server提供的資料,我們直接提供這些資料。 我們還從傳輸狀態中刪除了提供的資料,因此頁面的重新載入將不再使用提供的資料。

我們可以通過呼叫 hasKey 方法來檢測我們是在伺服器上還是在瀏覽器應用程式上。 此方法僅在瀏覽器中返回 true。

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<CityWeather> {
    const found = this.transferState.hasKey(RESULT_KEY);
    if (found) {
      const res = Observable.of(this.transferState.get<CityWeather>(RESULT_KEY, null));
      this.transferState.remove(RESULT_KEY);
      return res;
    } else {
      this.transferState.onSerialize(RESULT_KEY, () => this.result);
      const name = route.params['city'];
      return this.http.get<CityWeather>('https://api.openweathermap.org/data/2.5/weather?q=' + name + '&units=metric&APPID=' + this.key)
        .do(result => this.result = result);
    }
  }

因為我們是呼叫remove方法移除提供的資料,所以瀏覽器顯示的以下頁面會呼叫onSerialize方法,但是這個方法沒有效果,因為toJson只在服務端呼叫。

一個更清晰的解決方案是使用 isPlatformServer 和 isPlatformBrowser 方法來檢測平臺並採取相應的行動。

更多Jerry的原創文章,盡在:"汪子熙":