HTML のクライアントサイド レンダリングとインタラクティビティ

JavaScript による HTML レンダリングは、サーバーから送信された HTML のレンダリングとは異なるため、パフォーマンスに影響する可能性があります。このガイドでは、その違いと、ウェブサイトのレンダリング パフォーマンスを維持するためにできること(特にインタラクションに関連するもの)について説明します。

HTML の解析とレンダリングは、ブラウザの組み込みナビゲーション ロジックを使用するウェブサイトに対して、ブラウザがデフォルトで非常にうまく行うものです(「従来のページ読み込み」または「ハード ナビゲーション」と呼ばれることもあります)。このようなウェブサイトは、マルチページ アプリケーション(MPA)と呼ばれることもあります。

ただし、デベロッパーは、アプリケーションのニーズに合わせてブラウザのデフォルトを回避できます。これは、シングルページ アプリケーション(SPA)パターンを使用するウェブサイトに特に当てはまります。このパターンでは、JavaScript を使用してクライアント側で HTML/DOM の大部分が動的に作成されます。このデザイン パターンはクライアントサイド レンダリングと呼ばれ、処理が過剰になると、ウェブサイトのInteraction to Next Paint(INP) に影響する可能性があります。

このガイドでは、サーバーからブラウザに送信された HTML を使用する場合と、JavaScript を使用してクライアント上で HTML を作成する場合の違い、および後者によって重要なタイミングでインタラクション レイテンシが高くなる仕組みについて説明します。

ブラウザがサーバーから提供された HTML をレンダリングする方法

従来のページ読み込みで使用されるナビゲーション パターンでは、ナビゲーションのたびにサーバーから HTML を受信します。ブラウザのアドレスバーに URL を入力するか、MPA のリンクをクリックすると、次のイベントが発生します。

  1. ブラウザが指定された URL のナビゲーション リクエストを送信します。
  2. サーバーは HTML をチャンクで返します。

この最後のステップが重要です。また、サーバー/ブラウザのやり取りにおける最も基本的なパフォーマンス最適化の 1 つで、ストリーミングと呼ばれます。サーバーができるだけ早く HTML の送信を開始でき、ブラウザがレスポンス全体が届くのを待たない場合、ブラウザは HTML が届くたびにチャンクで処理できます。

サーバーから送信された HTML の解析を Chrome DevTools のパフォーマンス パネルで可視化したスクリーンショット。HTML がストリーミングされると、そのチャンクが複数の短いタスクで処理され、レンダリングは段階的に実行されます。
サーバーから提供された HTML の解析とレンダリング(Chrome DevTools のパフォーマンス パネルに表示されます)。HTML の解析とレンダリングに関連するタスクはチャンクに分割されます。

ブラウザで発生するほとんどの処理と同様に、HTML の解析はタスク内で行われます。HTML がサーバーからブラウザにストリーミングされると、ブラウザは、そのストリームのビットがチャンクで届くたびに、その HTML の解析を少しずつ行うことで、その HTML の解析を最適化します。その結果、ブラウザは各チャンクの処理後に定期的にメインスレッドに譲渡するため、長いタスクを回避できます。つまり、HTML の解析中に、ページをユーザーに表示するために必要な増分レンダリング作業や、ページの重要な起動期間中に発生する可能性のあるユーザー操作の処理など、他の処理が行われる可能性があります。このアプローチにより、ページの Interaction to Next Paint(INP)スコアが向上します。

つまり、サーバーから HTML をストリーミングすると、HTML の増分解析とレンダリングが自動的に行われ、メインスレッドに自動的に譲渡されます。クライアントサイド レンダリングでは、このメリットは得られません。

JavaScript によって提供された HTML をブラウザがレンダリングする仕組み

ページへの移動リクエストごとに、サーバーから一定量の HTML を提供する必要がある一方で、一部のウェブサイトでは SPA パターンを使用します。このアプローチでは、通常、サーバーが最小限の初期 HTML ペイロードを提供しますが、その後、クライアントはサーバーから取得したデータから組み立てられた HTML をページのメイン コンテンツ領域に入力します。その後のナビゲーション(この場合は「ソフト ナビゲーション」と呼ばれることもあります)は、すべて JavaScript によって処理され、ページに新しい HTML が入力されます。

クライアントサイド レンダリングは、JavaScript によって HTML が DOM に動的に追加される、より限定的なケースでは、SPA 以外でも発生することがあります。

JavaScript を使用して HTML を作成したり、DOM に追加したりする一般的な方法はいくつかあります。

  1. innerHTML プロパティを使用すると、文字列を介して既存の要素にコンテンツを設定できます。この文字列はブラウザによって DOM に解析されます。
  2. document.createElement メソッドを使用すると、ブラウザの HTML 解析を使用せずに、DOM に追加する新しい要素を作成できます。
  3. document.write メソッドを使用すると、ドキュメントに HTML を書き込むことができます(アプローチ 1 と同様に、ブラウザが解析します)。ただし、いくつかの理由により、document.write の使用は強くおすすめしません。
JavaScript でレンダリングされた HTML の解析を Chrome DevTools のパフォーマンス パネルで可視化したスクリーンショット。作業は、メインスレッドをブロックする単一の長いタスクで実行されます。
クライアント側で JavaScript を介して HTML を解析してレンダリングする。Chrome DevTools のパフォーマンス パネルに表示されます。解析とレンダリングに関連するタスクがチャンク化されていないため、長時間のタスクがメインスレッドをブロックします。

クライアントサイドの JavaScript を使用して HTML/DOM を作成すると、重大な結果を招く可能性があります。

  • ナビゲーション リクエストに応じてサーバーがストリーミングする HTML とは異なり、クライアント側の JavaScript タスクは自動的にチャンク化されないため、長いタスクがメインスレッドをブロックする可能性があります。つまり、クライアントで一度に作成する HTML/DOM が多すぎると、ページの INP に悪影響が及ぶ可能性があります。
  • 起動時にクライアントで HTML が作成された場合、その中で参照されるリソースは、ブラウザのプリロード スキャナによって検出されません。これは、ページの Largest Contentful Paint(LCP)に悪影響を及ぼします。これはランタイム パフォーマンスの問題ではありません(重要なリソースの取得におけるネットワーク遅延の問題です)。しかし、この基本的なブラウザのパフォーマンス最適化を回避することで、ウェブサイトの LCP に影響が出るのは避けたいものです。

クライアントサイド レンダリングによるパフォーマンスへの影響への対処方法

ウェブサイトがクライアントサイド レンダリングに大きく依存しており、フィールドデータの INP 値が低い場合、クライアントサイド レンダリングが問題に関係しているかどうか疑問に思うかもしれません。たとえば、ウェブサイトが SPA の場合は、フィールドデータから、レンダリング作業に多大な負荷をかけるインタラクションを見つけることができます。

原因が何であれ、問題を解決するために、考えられる原因をいくつかご紹介します。

サーバーからできるだけ多くの HTML を提供してください

前述のように、ブラウザはデフォルトで、サーバーからの HTML を非常に高いパフォーマンスで処理します。これにより、長いタスクを回避し、メインスレッドの合計時間を最適化するように、HTML の解析とレンダリングが分割されます。これにより、Total Blocking Time(TBT)が低下します。TBT は INP と強く相関しています

ウェブサイトの構築にフロントエンド フレームワークを使用している場合、その場合は、コンポーネントの HTML をサーバーでレンダリングするようにしてください。これにより、ウェブサイトに必要な初期のクライアントサイド レンダリングの量が制限され、エクスペリエンスが向上します。

  • React の場合は、Server DOM API を使用してサーバーで HTML をレンダリングする必要があります。ただし、従来のサーバーサイド レンダリング方法では同期アプローチが使用されるため、最初のバイトまでの時間(TTFB)が長くなることや、First Contentful Paint(FCP)や LCP などの後続の指標に影響する可能性があります。可能な場合は、Node.js またはその他の JavaScript ランタイムのストリーミング API を使用して、サーバーができるだけ早くブラウザへの HTML ストリーミングを開始できるようにしてください。React ベースのフレームワークである Next.js には、デフォルトで多くのベスト プラクティスが用意されています。サーバー上で HTML を自動的にレンダリングするだけでなく、ユーザーのコンテキスト(認証など)に基づいて変更されないページの HTML を静的に生成することもできます。
  • Vue は、デフォルトでクライアントサイド レンダリングも実行します。ただし、React と同様に、Vue でもコンポーネントの HTML をサーバーでレンダリングできます。可能な場合はこれらのサーバーサイド API を利用するか、ベスト プラクティスを簡単に実装できるように、Vue プロジェクトの上位レベルの抽象化を検討してください。
  • Svelte はデフォルトでサーバー上で HTML をレンダリングしますが、コンポーネント コードがブラウザ専用の Namespace(window など)にアクセスする必要がある場合、そのコンポーネントの HTML をサーバー上でレンダリングできないことがあります。不要なクライアントサイド レンダリングが発生しないように、可能な限り代替のアプローチを検討してください。SvelteKit(Next.js が React に相当する Svelte のフレームワーク)は、できるだけ多くのベスト プラクティスを Svelte プロジェクトに埋め込むため、Svelte のみを使用するプロジェクトで潜在的な落とし穴を回避できます。

クライアントで作成される DOM ノードの数を制限する

DOM が大きいほど、レンダリングに必要な処理が増える傾向があります。ウェブサイトが本格的な SPA であるか、MPA のインタラクションの結果として既存の DOM に新しいノードを挿入しているかにかかわらず、これらの DOM はできるだけ小さくすることを検討してください。これにより、その HTML を表示するためにクライアントサイド レンダリング中に必要な作業が減り、ウェブサイトの INP を低く抑えることができます。

ストリーミング サービス ワーカーのアーキテクチャを検討する

これは高度な手法であり、すべてのユースケースで簡単に機能するとは限りません。しかし、ユーザーがページ間を移動するときに即座に読み込まれるように MPA をウェブサイトに変換できる手法です。サービス ワーカーを使用してウェブサイトの静的部分を CacheStorage にプリキャッシュし、ReadableStream API を使用してページの残りの HTML をサーバーから取得できます。

この手法がうまく機能すると、クライアントで HTML を作成することはありませんが、キャッシュからコンテンツの部分が即座に読み込まれるため、サイトの読み込みが速い印象を与えます。このアプローチを使用するウェブサイトは、クライアントサイド レンダリングのデメリットがない SPA のように見えます。また、サーバーからリクエストする HTML の量も削減されます。

つまり、ストリーミング サービスのワーカー アーキテクチャは、ブラウザの組み込みナビゲーション ロジックを置き換えるのではなく、それに追加するものです。Workbox でこれを実現する方法については、ストリームを使用したマルチページ アプリケーションの高速化をご覧ください。

まとめ

ウェブサイトが HTML を受信してレンダリングする方法は、パフォーマンスに影響します。ウェブサイトの機能に必要な HTML のすべて(または大部分)をサーバーが送信する場合、増分解析とレンダリング、長いタスクを回避するためのメインスレッドへの自動譲渡など、多くのメリットが得られます。

クライアントサイドの HTML レンダリングでは、多くの場合回避可能なパフォーマンスの問題がいくつか発生します。ただし、個々のウェブサイトの要件によっては、100% 回避できるとは限りません。クライアントサイドのレンダリングの過剰によって発生する可能性のある長いタスクを軽減するには、可能な限りウェブサイトの HTML をサーバーから送信し、クライアントでレンダリングする必要がある HTML の DOM サイズをできるだけ小さくします。また、クライアントへの HTML の配信を高速化するために、代替のアーキテクチャを検討し、サーバーから読み込まれた HTML に対してブラウザが提供する増分解析とレンダリングを活用します。

ウェブサイトのクライアントサイド レンダリングを可能な限り最小限に抑えることができれば、ウェブサイトの INP だけでなく、LCP、TBT などの他の指標も改善され、場合によっては TTFB も改善される可能性があります。

UnsplashMaik Jonietz によるヒーロー画像。