BT

InfoQ ホームページ ニュース 新しいFacebook.comの最適化戦略 - React ConfでのAshley Watkins氏の講演

新しいFacebook.comの最適化戦略 - React ConfでのAshley Watkins氏の講演

ブックマーク

原文(投稿日:2020/04/29)へのリンク

Ashley Watkins氏は、React Confで、FB5、新しい facebook.com を強化するテクノロジーと戦略のいくつかについて話し合い、データ駆動の依存、段階的なコードとデータのダウンロードなどのトピックに取り組んだ。

新しい facebook.com Webサイトは、React/GraphQL/Relayスタック上に構築されたシングルページのWebアプリケーションである。GraphQLは、開発者が必要なデータを指定するためのクエリ言語である。Relayは、GraphQLクエリを構築するためにReactと統合し、データフェッチを処理するフレームワークである。Watkins氏は、Facebookの技術スタックを前の3つの技術に基づいて標準化することで、Facebookはユーザエクスペリエンスを最適化しながら大規模な運用方法を再考することができたと説明した。

シングルページアプリケーションの主な課題は、視覚的に完了するまでの時間、つまりユーザがWebサイトに移動してからスクロールせずに見える範囲のコンテンツが表示されるまでにかかる時間を最小限に抑えることである。シングルページアプリの標準的な実装では、クライアントがページをリクエストし、サーバがページのHTMLとJavaScriptを送信します。これにより、一部のデータのフェッチがトリガされる。データの取得中は、ロード画面が表示される。

新しい facebook.com アーキテクチャでは、ブラウザによって要求されたHTMLドキュメントがクライアントにストリーミングされる。その際、ブラウザはドキュメントを段階的に解析し、HTMLファイルで参照されているスクリプトのダウンロードを開始する。Facebookサーバは、すべてのユーザが必要とするCSSおよびJavaScriptリソースで開始し、計算が遅いページ固有およびユーザ固有のリソースが続行するようにHTMLページを書き込む。その時点で、サーバはRelayによって示されるようにデータのフェッチを開始できる。HTMLページは、Relayによってプリフェッチされたデータが利用可能になると、そのデータを含むscriptタグで終了する。その結果、JavaScriptとデータが並行してダウンロードされる。したがって、この最適化されたプロセスは、HTMLフラッシュと、ページに必要なデータのGraphQLによる一元化された記述によって可能になる。最良の場合、時間の節約により、ロード画面を表示する必要がなくなる可能性がある。

Facebookは、最初のペイントまでの時間も最適化する。これは、画面に最初のピクセルを表示するのに必要な時間である (したがって、視覚的に完了するまでの時間よりも必然的に短くなる)。そのために、Facebookは段階的なコード分割を使用している。段階的なコード分割では、コードは優先度が高くなる3つのバケットに分割され、3つの段階でダウンロードされる。最初のバケットは読み込みページに関連し、2番目のバケットにはページに視覚的に影響を与える必要な情報が含まれ、3番目のバケットは懸念事項の表示(分析など)に直交するコードとデータを収集する。Facebookは、コンポーネントをフェーズに割り当てるために、importForDisplay() (フェーズ2) とimportForAfterDisplay() (フェーズ3) の2つのAPIを追加した。最初の2つのフェーズのそれぞれの終わりに、レンダリングが発生する。したがって、最初のレンダリングは、ページの完全なコードがフェッチされる前に行われ、最初のペイントまでの時間が短縮される。フェーズ3には画面に影響を与えない情報のみが組み込まれているため、フェーズ2の終わりまでに画面が完成し、視覚的に完成するまでの時間も短縮される。

Facebookはさらに、意味のあるペイントまでの時間を最小限に抑えることで、プライマリコンテンツをできるだけ早く取得するように最適化する。以前の最適化戦略にはスマートなコード分割が含まれていたが、意味のあるペイントまでの時間の最適化戦略にはデータ分割が含まれている。この戦略では、重要なデータが最初に到着し、すぐに使用される必要がある。たとえば、ページの最初のレンダリングで複数のニュースフィード投稿を表示する必要があることはめったにない。データ分割をサポートするために、Relayはストリーミングクエリを導入した。このクエリでは、ストリーミングできるクエリ部分に@streamマーカの注釈が付けられる:

fragment HomepageData on User {
  newsFeed(first: 10) (
    edges @stream
  }
  -- Additional data @defer
}

@deferアノテーションを使用すると、クエリされたデータのどの部分が重要でないかを示すこともできる。したがって、意味のあるペイントが早く来て、追加のデータが受信されると、ビューがハイドレイトされ、画面が更新される。さらに、最初に配信される重要なコードとデータは通常、スクロールせずに見える範囲のコンテンツに関連しているため、視覚的に完了するまでの時間も短縮される可能性がある。

最後の最適化戦略は、使用されないコードをフェッチしないことで構成される。Watkins氏は、A/Bテストの目的で発生する可能性がある、同じコンポーネントの2つのバリエーションの例を示した。2番目のコンポーネントバージョンは、個別の機能を提供し、ユーザがA/Bグループで選択された場合にのみ必要な追加コードが付属している。Watkins氏は、最初のアイデアである動的インポートでフィーチャートグルを実装することは、ストリーミングレンダリングとうまく調和しないことに気づいた。動的インポートは、以前にダウンロードしたコードをクライアントで実行した結果である。これは、コードが解析および実行されている間、連続する動的インポートの間にギャップがあり、レンダリングがさらに遅れることを意味する。Facebookはフィーチャートグルを実装しているため、サーバがページリクエストを受信したときに検出できるようになり、サーバは適切なコードのみを送信する。

もう1つのケースは、複数のバリアントを持つコンポーネントのケースである。そのうちの1つは、フェッチされたデータに応じて動的に選択される。たとえば、Postコンポーネントは、フェッチされた投稿のタイプまたはコンテンツに応じて、VideoPostまたはPhotoPostコンポーネントに委任する場合がある。これらの各コンポーネントには、独自のデータ要件がある場合がある。したがって、標準の実装では、すべてのバリアントのコードとデータがダウンロードされる。この場合、facebook.com は、データ駆動コード分割と呼ばれる最適化戦略を適用する。これは、バリアントコンポーネントのクエリと @module RelayアノテーションのGraphQL記述に依存する。

... on Post {
  ... on PhotoPost {
    @module('PhotoComponent.js')
    photo_data
  }
  ... on VideoPost {
    @module('VideoComponent.js')
    video_data
  }  
  ... on SongPost {
    @module('SongComponent.js')
    song_data
  }  
}

コードとデータが並行してストリーミングされると、ページは段階的にレンダリングされ、データと画像が異なる時間に到着するときにコンテンツが移動しないように、コンポーネントのレンダリングを調整する必要がある。facebook.com Webサイトの開発者は、調整の目的で React Suspense を活用し、知覚されるユーザの読み込みエクスペリエンスを最適化する。

Watkins氏の全講演はReactConfのサイトで入手でき、さらにコードスニペットと詳細な説明が含まれている。React Confは、公式のFacebook Reactイベントである。React Confは、2019年10月24日と25日にネバダ州ヘンダーソンで開催された。

この記事に星をつける

おすすめ度
スタイル

こんにちは

コメントするには InfoQアカウントの登録 または が必要です。InfoQ に登録するとさまざまなことができます。

アカウント登録をしてInfoQをお楽しみください。

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

コミュニティコメント

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

BT

あなたのプロファイルは最新ですか?プロフィールを確認してアップデートしてください。

Eメールを変更すると確認のメールが配信されます。

会社名:
役職:
組織規模:
国:
都道府県:
新しいメールアドレスに確認用のメールを送信します。このポップアップ画面は自動的に閉じられます。