ローディングUIとストリーミング
特殊ファイルloading.js
はReact Suspenseを使用して意味のあるローディングUIを作成するのに役立ちます。この規約により、ルートセグメントのコンテンツがロードされている間、サーバーから即時ローディング状態を表示できます。レンダリングが完了すると、新しいコンテンツが自動的に入れ替わります。
即時ローディング状態
即時ローディング状態とは、ナビゲーション時に即座に表示されるフォールバックUIのことです。スケルトンやスピナーなどのローディングインジケーター、または将来の画面の一部(カバー写真やタイトルなど)を事前にレンダリングできます。これによりユーザーはアプリが応答していることを理解でき、より良いユーザー体験を提供します。
フォルダ内にloading.js
ファイルを追加することでローディング状態を作成できます。
export default function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return <LoadingSkeleton />
}
同じフォルダ内では、loading.js
はlayout.js
の内部にネストされます。これによりpage.js
ファイルとその下の子要素は自動的に<Suspense>
境界でラップされます。
補足:
- サーバー中心のルーティングを使用していても、ナビゲーションは即時に行われます。
- ナビゲーションは中断可能であり、ルートの変更時に現在のルートのコンテンツが完全にロードされるのを待つ必要はありません。
- 新しいルートセグメントがロードされている間も、共有レイアウトはインタラクティブな状態を維持します。
推奨: Next.jsはこの機能を最適化しているため、ルートセグメント(レイアウトとページ)には
loading.js
規約を使用してください。
Suspenseによるストリーミング
loading.js
に加えて、独自のUIコンポーネント用にSuspense境界を手動で作成することもできます。App RouterはSuspenseによるストリーミングをサポートしています。
補足:
- 一部のブラウザはストリーミングレスポンスをバッファリングします。レスポンスが1024バイトを超えるまでストリーミングレスポンスが表示されない場合があります。これは通常、「hello world」アプリケーションにのみ影響し、実際のアプリケーションには影響しません。
ストリーミングとは何か?
ReactとNext.jsでストリーミングがどのように機能するかを理解するには、**サーバーサイドレンダリング(SSR)**とその制限について知ることが役立ちます。
SSRでは、ユーザーがページを見て操作できるようになるまでに、一連のステップを完了する必要があります:
- まず、特定のページのすべてのデータがサーバー上でフェッチされます。
- サーバーはページのHTMLをレンダリングします。
- ページのHTML、CSS、JavaScriptがクライアントに送信されます。
- 生成されたHTMLとCSSを使用して、非インタラクティブなユーザーインターフェースが表示されます。
- 最後に、Reactがユーザーインターフェースをハイドレートしてインタラクティブにします。
これらのステップは順次的でブロッキングであり、すべてのデータがフェッチされるまでサーバーはページのHTMLをレンダリングできません。また、クライアント側では、ページ内のすべてのコンポーネントのコードがダウンロードされるまで、ReactはUIをハイドレートできません。
ReactとNext.jsを使用したSSRは、できるだけ早く非インタラクティブなページをユーザーに表示することで、体感的な読み込みパフォーマンスを向上させます。
しかし、サーバー上でのすべてのデータフェッチがページの表示前に完了する必要があるため、依然として遅い場合があります。
ストリーミングを使用すると、ページのHTMLを小さなチャンクに分解し、それらのチャンクをサーバーからクライアントに段階的に送信できます。
これにより、すべてのデータがロードされるのを待たずに、ページの一部を先に表示できるようになります。
ストリーミングはReactのコンポーネントモデルと相性が良いです。各コンポーネントはチャンクとみなすことができるからです。優先度の高いコンポーネント(製品情報など)やデータに依存しないコンポーネント(レイアウトなど)を先に送信でき、Reactは早期にハイドレーションを開始できます。優先度の低いコンポーネント(レビュー、関連商品など)は、データのフェッチ後に同じサーバーリクエストで送信できます。
ストリーミングは、長いデータリクエストがページのレンダリングをブロックするのを防ぎたい場合に特に有効で、Time To First Byte (TTFB)とFirst Contentful Paint (FCP)を短縮できます。また、特に遅いデバイスでのTime to Interactive (TTI)の改善にも役立ちます。
例
<Suspense>
は、非同期アクション(データのフェッチなど)を実行するコンポーネントをラップし、そのアクションが実行されている間はフォールバックUI(スケルトン、スピナーなど)を表示し、アクションが完了するとコンポーネントに置き換えるという仕組みです。
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'
export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}
Suspenseを使用することで、以下のメリットが得られます:
- ストリーミングサーバーレンダリング - サーバーからクライアントへHTMLを段階的にレンダリングします。
- 選択的ハイドレーション - Reactはユーザーのインタラクションに基づいて、最初にインタラクティブにするコンポーネントの優先順位を決定します。
Suspenseの詳細な例やユースケースについては、Reactのドキュメントを参照してください。
SEO
- Next.jsは
generateMetadata
内のデータフェッチが完了するまで待ってからUIをクライアントにストリーミングします。これにより、ストリーミングレスポンスの最初の部分に<head>
タグが含まれることが保証されます。 - ストリーミングはサーバーレンダリングされるため、SEOに影響はありません。Googleのウェブクローラーにページがどのように表示されるかを確認し、シリアライズされたHTMLを表示するには、GoogleのRich Results Testツールを使用できます(出典)。
ステータスコード
ストリーミング時には、リクエストが成功したことを示す200
ステータスコードが返されます。
サーバーは、ストリーミングされるコンテンツ自体の中で、例えばredirect
やnotFound
を使用する場合など、クライアントにエラーや問題を伝えることができます。レスポンスヘッダーはすでにクライアントに送信されているため、レスポンスのステータスコードを更新することはできません。これはSEOには影響しません。