Next.js の loading と Suspense。最小構成サンプル
待たせ方には2つあります。
動いているものを触る/demo/loadingファイル構成
並んでいるのは、上の画面をいま動かしている本物のファイルです。名前を押すと中身が出ます。
ファイル構成
- app/
app/layout.tsxBuild
全ページの外側。本物のプロジェクトでは html と body をここで書きます
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "待たせる画面の最小構成",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return <main>{children}</main>;
}- .tsx
- 画面を返すファイル。HTML のような書き方ができます
- .ts
- 画面を返さないファイル。処理とデータだけを置きます。ここに画面を書くと原因を名指ししないエラーで落ちます
- Build
- ビルド時に一度だけ動きます
- Server
- アクセスのたびにサーバーで動きます
- Browser
- 利用者の手元で動きます
待たせ方は2つある
| 書くもの | 待たせる範囲 | いつ出るか |
|---|---|---|
| loading.tsx | ページ全体 | そのページへ移るとき |
| Suspense | 囲んだ中だけ | そのページを描いている最中 |
loading.tsx はファイルを置くだけです。自分で Suspense を書く必要はありません。Next.js がそのページを囲んでくれます。
待たせる前に、待つ必要があるか
このサンプルには1行だけ、変わった行があります。
export const dynamic = "force-dynamic";
これが無いと、待ち時間はビルドの中で終わります。
実機で確かめました。この行を書く前は、0.6 秒の待ちがビルド時に消化され、出来上がった HTML に値が焼き込まれていました。開いた人は待ちません。待たせる話をする前に、そもそも待つ必要がなかったわけです。
遅い部分だけを囲む
<p>この行はすぐに出ます。</p>
<Suspense fallback={<p>この部分だけ読み込んでいます</p>}>
<Slow />
</Suspense>
実機で確かめました。応答は 0.69 秒かかりますが、届く順番が違います。
1つ目に届いたもの この行はすぐに出ます
2つ目に届いたもの この部分だけ読み込んでいます ← 待っている間の表示
3つ目に届いたもの 0.6秒かけて取ってきた値 ← 0.6秒後に追いついた
囲まなければ、この 0.6 秒のあいだページに何も出ません。囲めば、速い部分だけ先に出ます。
- Server速い部分を先に送る
遅い部分の場所には、待っている間の表示を入れておく。
- Browser先に届いた分を描く
読める部分はここでもう読める。
- Server遅い部分が終わったら送る
同じ1回の通信の続きとして届く。
- Browserその部分だけ差し替わる
ページは開き直されない。
2つの使い分け
ページ全体を待たせる
// app/loading.tsx を置くだけ export default function Loading() { return <p>読み込んでいます</p> }
移った瞬間に何か出る。ただし全部が揃うまで中身は出ない。
遅い部分だけを待たせる
// 遅いところだけ囲む <p>すぐ出る部分</p> <Suspense fallback={<p>待機中</p>}> <Slow /> </Suspense>
速い部分は先に出る。囲む範囲を小さくするほど、早く読める。
両方を書いて構いません。このサンプルは両方あります。移った瞬間は loading.tsx が出て、ページが描かれ始めると遅い部分だけが Suspense で待たされます。
待ち時間は短く
ここから足していくとき
- 骨組みを見せる。
fallbackに四角を並べて、出来上がりの形を先に見せます - 失敗したときの表示。取得が失敗する経路には
error.tsxを置きます。そちらは Client Component です - 囲む範囲を分ける。遅いものが複数あるなら、別々に囲むと早いものから出ます
囲む範囲を小さくするほど、読める時間が早くなります。ページ全体を囲むのは、いちばん粗い使い方です。
入れていないもの
このまま本番に持っていかないでください。省いてあるのは次の点です。
- 本物の遅い処理。0.6秒待つふりをしているだけです
- 失敗したときの表示。取得が失敗する経路を作っていません
- 待ち時間の計測。どこで時間がかかったかは残していません
- 骨組みの表示。四角を並べて待たせる形にはしていません
出典
- nextjs.org/docs/app/api-reference/file-conventions/loading
- nextjs.org/docs/app/getting-started/fetching-data
- react.dev/reference/react/Suspense
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。