アングルサーバ側レンダリング(SSR):ブラウザはサーバーではありません



SSRについての大きなことの一つは、我々は我々のフロントエンドと私たちのバックエンドで我々のアプリをレンダリングするために同じコードを使用するようになることです.まあ、ソート.
我々が同じコードを使うとき、我々には問題があります:ブラウザーはサーバーでありません、そして、我々が各々の環境でできることの違いがあります.
サーバー上で我々の角度アプリをレンダリングする利点は、我々がブラウザに何かを送信する前に、個人的かつ効率的にデータを取得することができますです.
私たちのサーバーは(この場合)ノードです.JSなどのサーバが利用できます.
  • サーバがHTTPリクエストを取得するリクエスト(
  • fsファイルシステムにアクセスする(必要なら)
    あなたがサーバーで望む他の何かへのアクセス:Redis、AWSサービス、データベースなど.
  • しかし、ブラウザはサーバではありません.そして、我々がブラウザだけをAPIと呼ぶならば、我々はSSRを壊します.

    何がSSRを破ることができますか?


    さて、ブラウザには三つのことが考えられます.
  • ウィンドウは、例えば、ユーザーに警告を表示するために用いることができる
  • ドキュメントはウィンドウ名前空間に属し、DOM要素を操作するために使用されます
  • 「ナビゲータ」ウィンドウの名前空間に属し、プログレッシブWebアプリケーションで広範囲に使用されるサービスワーカーを有効にします
  • 我々の角度アプリケーションがサーバーとブラウザーの間でコードを共有することができることは素晴らしいですが、これらのオブジェクトのどれかを使いたいなら、現在の実行時:ノードに基づいて異なる論理パスを実行する必要があります.JSまたはブラウザウィンドウ.
    以下は、私はあなたにそれを行うためのテクニックの一つを示すつもりです

    国際化の追加


    アプリケーションに国際化を加えましょう.つの通貨で製品価格を表示しましょう:米ドル、英国ポンド、ポーランドZloty.アプリケーションは、ブラウザの設定に基づいて通貨を選択する必要がありますし、指定された言語がサポートされていない場合、それはポーランドZloty
    新しいサービスを生成しましょうng g s sampleユーザー言語を検出し、3つの利用可能な通貨コードのうちの1つを返すgetCurrencyCode ()メソッドを実装しましょう.
      providedIn: 'root'
    })
    export class SampleService {
    
      private userLang;
    
      constructor() { 
          this.userLang = window.navigator.language;
      }
    
      public getCurrencyCode(): string {
        switch(this.userLang) {
          default: 
          case 'pl-PL': return 'PLN';
          case 'en-US': return 'USD';
          case 'en-EN': return 'GBP';
        }
      }
    }
    
    コンポーネントのいずれかで、PropertDetailsComponentというように、このサービスを使用してユーザーの通貨を取得できます.
    public userCurrency: string = this.sampleService.getCurrencyCode();
    
    constructor(
      private route: ActivatedRoute, 
      private ps: ProductsService, 
      private us: UserService, 
      private sampleService: SampleService
    ) { }
    
    次に、通貨パイプでビューでユーザー通貨を使用できます.<pclass="text-muted">{{userCurrency}}</p>今後、価格はユーザーのローカライズ設定によって定義された通貨で表示する必要があります.これは素晴らしいですか?

    残念ながら、このロジックはSSRを壊します。

    ERROR: ReferenceError: window is not defined現在のランタイムがブラウザかサーバであるかどうかを検出する機構があれば、それが役に立つでしょう.

    isplatformbrowser ()およびisplatformserver ()


    @ angle/commonパッケージのisplatformbrowser ()およびisplatformserver ()メソッドを持つ角の船.これらのメソッドの各々は、1つのパラメタを受け入れます:プラットホームID.
    上記の国際化サービスI 18 NServiceを変更するには、次の新しいインポートを追加します.
    import { 
      Injectable, 
      Inject, 
      PLATFORM_ID 
      } from '@angular/core';
    import { 
      isPlatformBrowser 
      } from '@angular/common';
    
    サービスコンストラクタを変更して、サービスのインスタンスがブラウザで実行された場合にのみウィンドウオブジェクトを使用します.
    export class SampleService {
      constructor(
        @Inject(PLATFORM_ID)
        private platformId: any
      ) {
        if (isPlatformBrowser(this.platformId)) {
          this.userLang =
            window.navigator.language;
        } else {
          // server specific logic
        }
      }
      // ...
    } 
    
    これはSSRが再び動作を開始するのに十分なはずですが、サーバー側のレンダリングにプリプレインされた国際化を取得しません.国際化はアプリケーションのロード後に表示されません.
    それで、我々が必要とするものは、起源HTTPリクエストからサーバーにどんな言語を与えるかを知る方法です.
    リクエストオブジェクト
    問題は、サーバー上のユーザー言語に関する情報を取得する方法です.それは可能ですか?
    はい.
    ブラウザから要求を実行しているときは、ブラウザは通常、あなたが考えないかもしれないHTTPヘッダの束を追加します.
    これらのヘッダーの1つは、ユーザーが望む言語を教えて受け入れる言語です!
    例えば、ヘッダは以下のようにやってくるかもしれません.q = 0.5

    リクエストからヘッダを取得する


    角のUniversalを使用すると、HTTPリクエストを表すオブジェクトを取得できます.@ ngUniversal/Express Engine/Tokensパッケージからリクエストトークンの下で依存関係インジェクションによって利用可能です.requestオブジェクトは以下のフィールドを含みます:
  • ボディ
  • パーム
  • ヘッダ
  • クッキー
  • リクエストオブジェクト、リクエストインジェクショントークン、およびオプションのデコレータを追加することで、インポートを更新します
    import { Injectable, Inject, PLATFORM_ID, Optional } from '@angular/core';
    import { isPlatformBrowser } from '@angular/common';
    import { REQUEST } from '@nguniversal/express-engine/tokens';
    import { Request } from 'express';
    
    Constructorを変更して、Accept Languageヘッダーから要求オブジェクトを入力し、ユーザー言語を取得します
    export class SampleService {
      constructor(
        @Inject(PLATFORM_ID) private platformId: any,
        @Optional()
        @Inject(REQUEST) private request: Request
      ) {
        if (isPlatformBrowser(this.platformId)) {
          this.userLang =
            window.navigator.language;
        } else {
          this.userLang = (
            this.request.headers[
              "accept-language"
            ] || ""
          ).substring(0, 5);
        }
      }
      // ...
    }