Next.jsのレンダリングについては、「どう設定すべきかは業務要件やアーキテクチャ特性による」みたいな解説が多く見受けられる気がしました。
もちろんそうだと思うのですが、設計例があるだけでずっととっつきやすくなると思いますので、この記事では設計例も交えながら、ISRとfallbackについて解説したいと思います。
前半でISRとfallbackについて解説し、後半で設計例について解説します。
想定読者
SSGのレンダリング種別について理解していることが前提です。
「next build
時にビルドサーバー上でgetStaticProps
に記載されたロジックを実行してデータを取得する。ビルドの成果物としてデータを含めることで、実行時にはデータソースへのアクセスなしで画面を表示できる」くらいの理解度で問題ありません。
また、Next.jsでの静的生成の正式名称はStatic Geneartionとなりますが、本記事内では、一般的な用語に合わせてSSGで統一します。
ISRとfallback
1. 全体感
ざっくりISRとfallbackの全体感を掴むための図解は以下。
- どちらもSSGに関する概念
- fallbackは動的ルーティングするページにのみ関係する。実際に、
getStaticPaths
内で指定する設定値である。
- ISRはデータが古くなっていないかの確認のため、静的ルーティング/動的ルーティング両方のページに指定する。実際に、
getStaticProps
内で指定する設定値である。
- ISRもfallbackも、ページの追加や更新の処理は
getStaticProps
の処理を流用している。
- ので、ISRやfallbackを指定する場合、例えば
getStaticProps
で環境変数を使っていれば、ビルドサーバーだけではなく実際のNext.jsサーバーにも環境変数が必要となる。他にも、データソース側にIP制限をかけている場合なども同様である。
ISRとfallbackはこのような棲み分けとなっており、それぞれ独立して設定可能である。
2. ISRについて
- 概要
- ISRは、データが古くなっていないかの確認をする。
- SSGを行う全てのページに設定でき、
getStaticProps
内でrevalidate
プロパティで間隔を設定する。
- つまり、
revalidate
プロパティに値が指定されているのであれば、そのページはISRされるということになる。
- データ取得の最小間隔
- そのページが生成されてから、
revalidate
に指定した秒数経つまで
- データ取得の発生タイミング
- データアクセスを最小限にするため、
revalidate
に指定した秒数が経ってから、当該ページが取得されたタイミングで行われる。
- 例:間隔が30秒なら、そのページが生成されてから30秒経ってから最初に受けたリクエスト時にデータ更新を行う
- 返されるページとの関係性
- 遅延を起こさないため、ページの返却とは無関係に行われる。
- 例:1つ上の例では、30秒経ってから最初に受けたリクエストに対しては、その時点のページが返される
3. fallback
- 概要
- fallbackは、動的ルーティングを行うページについて、生成していないパスに対するリクエストが来たときにページを生成するかエラー扱いにするかを設定する。
- SSGを行うページのうち、動的ルーティングを行うページに設定でき、
getStaticPaths
内のfallback
プロパティに有効化有無を設定する。
- 有効化する場合、
fallback:true
とfallback:blocking
の設定が存在する。これは、loadingのような画面描画を挟むか、同期的にページを生成するかといった違いになる。
- データ取得の最小間隔
- 既に存在するページに対しては発生しないため、間隔といった概念は存在しない。
- データ取得の発生タイミング
- 返されるページとの関係性
fallback:true
とfallback:blocking
の設定がある。
fallback:true
とした場合は、一度「loading...」のような描画を挟んで、非同期で遷移が可能。
- 仕組みとしては、router.isFallbackを判定してページ生成中のエレメントを記述しておく。fallbackが発動したときは、ページを即座に返却し、フロント側でisFallback:trueのエレメントが表示しつつ、画面遷移時同様の(ページ名).jsonリクエストが即座に行われる。(ページ名).jsonが返ってきたタイミングでisFallback:trueになったり、Propsがundefinedでなくなったりすることで、ユーザー目線の初期表示が完了する。
- この動作はページ再読み込みのときだけである。next/linkなどでのクライアントサイド画面遷移については、挙動は
fallback: blocking
と同等となる。
fallback:blocking
とした場合は、SSRと同様に、同期的にページを生成して返却する。
fallback: false
とした場合
- 生成していないパスにリクエストが来た場合、データ取得や生成は行われず、404エラーとなる。
設計例
1. 題材
ブログ一覧とブログ詳細ページを想定する。
- ブログ詳細ページは、記事のIDがURLのパスに含まれているものとする。
- ブログ投稿は追加や変更がありえるものの、頻繁ではないものとする。
2. 結論
今回は以下のように設計した。
- ブログ一覧ページ:SSG、ISRあり、fallbackなし
- ブログ詳細ページ:SSG、ISRあり、fallbackあり
3. 解説
- ブログ一覧ページ
- ISR:データソースに対するブログ投稿全件のクエリの結果が変わる可能性がある(ブログ投稿の追加・変更)。ISRなしとすると、再度デプロイを行うまで一覧上にブログ投稿の追加・変更が反映されない。そこで、ISRはありとする。
- fallback:動的ルーティングが不要なページとなるので、getStaticPathsを定義せず、fallbackは指定しない
- ブログ詳細ページ
- ISR:データソースに対するブログ投稿1件のクエリ結果が変わる可能性がある(ブログ投稿の変更)。ISRなしとすると、再度デプロイを行うまで詳細ページにブログ投稿の変更が反映されない。そこで、ISRはありとする。
- fallback:ブログ記事ごとに異なるパスとなるため、getStaticPathsが定義される。一覧ページにISRを指定したため、fallbackは有効化されるべきである。そうしなければ、新規投稿が一覧ページに表示されても、ブログ詳細ページに遷移したときにエラーとなってしまうためである。
- fallbackの指定は
true
でも"blocking"
でも機能上は問題ない。実現したいユーザー体験に応じて選んでよい。