Next.jsを使用したインクリメンタル静的再生(ISR)の完全ガイド
公開: 2022-03-101年前、Next.js 9.3は静的サイト生成(SSG)のサポートをリリースし、最初のハイブリッドフレームワークになりました。 私はこの時点で約数年間幸せなNext.jsユーザーでしたが、このリリースによりNext.jsが私の新しいデフォルトソリューションになりました。 Next.jsを幅広く使用した後、私はVercelに参加して、TripadvisorやWashingtonPostなどの企業がNext.jsを採用および拡張するのを支援しました。
この記事では、Jamstackの新しい進化であるIncremental Static Regeneration(ISR)について説明します。 以下に、ユースケース、デモ、トレードオフなど、ISRのガイドを示します。
静的サイト生成の問題
Jamstackの背後にある考え方は魅力的です。つまり、事前にレンダリングされた静的ページをCDNにプッシュして、数秒でグローバルに利用できるようにすることです。 静的コンテンツは高速で、ダウンタイムに強く、クローラーによって即座にインデックスが作成されます。 しかし、いくつかの問題があります。
大規模な静的サイトの構築中にJamstackアーキテクチャを採用した場合、サイトの構築を何時間も待たされる可能性があります。 ページ数を2倍にすると、ビルド時間も2倍になります。 Target.comについて考えてみましょう。 展開ごとに数百万の製品を静的に生成することは可能ですか?
すべてのページが非現実的な1ミリ秒で静的に生成されたとしても、サイト全体を再構築するには数時間かかります。 大規模なWebアプリケーションの場合、完全な静的サイト生成を選択することは簡単ではありません。 大規模なチームには、より柔軟でパーソナライズされたハイブリッドソリューションが必要です。
コンテンツ管理システム(CMS)
多くのチームにとって、サイトのコンテンツはコードから切り離されています。 ヘッドレスCMSを使用すると、コンテンツ編集者は開発者を関与させることなく変更を公開できます。 ただし、従来の静的サイトでは、このプロセスが遅くなる可能性があります。
100,000個の商品を扱うeコマースストアを考えてみましょう。 製品の価格は頻繁に変更されます。 コンテンツエディタがプロモーションの一環としてヘッドフォンの価格を100ドルから75ドルに変更すると、CMSはWebhookを使用してサイト全体を再構築します。 新しい価格が反映されるまで何時間も待つことは現実的ではありません。
不必要な計算を伴う長いビルドでも、追加の費用が発生する可能性があります。 理想的には、アプリケーションは、どの製品が変更されたかを理解し、完全な再構築を必要とせずにそれらのページを段階的に更新するのに十分インテリジェントです。
インクリメンタルスタティックリジェネレーション(ISR)
Next.jsを使用すると、サイトを構築した後に静的ページを作成または更新できます。 インクリメンタル静的再生(ISR)を使用すると、開発者とコンテンツ編集者は、サイト全体を再構築しなくても、ページごとに静的生成を使用できます。 ISRを使用すると、数百万ページにスケーリングしながら、静的の利点を維持できます。
静的ページは、ISRを使用したビルド時ではなく、実行時に(オンデマンドで)生成できます。 分析、A / Bテスト、またはその他のメトリックを使用して、ビルド時間で独自のトレードオフを行う柔軟性を備えています。
100,000個の製品を扱う以前のeコマースストアについて考えてみます。 各商品ページを静的に生成するための現実的な50ミリ秒では、ISRがないと約2時間かかります。 ISRでは、次から選択できます。
- より高速なビルド
ビルド時に最も人気のある1,000個の製品を生成します。 他の製品に対して行われたリクエストはキャッシュミスであり、オンデマンドで静的に生成されます:1分のビルド。 - より高いキャッシュヒット率
ビルド時に10,000個の製品を生成し、ユーザーの要求(8分間のビルド)の前にさらに多くの製品がキャッシュされるようにします。
eコマース製品ページのISRの例を見ていきましょう。
入門
データの取得
これまでNext.jsを使用したことがない場合は、「Next.js入門」を読んで基本を理解することをお勧めします。 ISRは、同じNext.js APIを使用して静的ページを生成します: getStaticProps
。 revalidate revalidate: 60
を指定することにより、このページにISRを使用するようにNext.jsに通知します。
- Next.jsは、ページごとの再検証時間を定義できます。 60秒に設定しましょう。
- 商品ページへの最初のリクエストでは、キャッシュされたページが元の価格で表示されます。
- 製品のデータはCMSで更新されます。
- 最初のリクエストから60秒前までのページへのリクエストはすべてキャッシュされ、瞬時に行われます。
- 60秒のウィンドウの後、次のリクエストにはキャッシュされた(古い)ページが表示されます。 Next.jsは、バックグラウンドでページの再生成をトリガーします。
- ページが正常に生成されると、Next.jsはキャッシュを無効にし、更新された製品ページを表示します。 バックグラウンドの再生成が失敗した場合、古いページは変更されません。
// pages/products/[id].js export async function getStaticProps({ params }) { return { props: { product: await getProductFromDatabase(params.id) }, revalidate: 60 } }
パスの生成
Next.jsは、ビルド時に生成する製品とオンデマンドで生成する製品を定義します。 getStaticPaths
に上位1,000個の製品IDのリストを提供することにより、ビルド時に最も人気のある1,000個の製品のみを生成しましょう。
最初のビルド後に他の製品をリクエストするときに、Next.jsがどのように「フォールバック」するかを構成する必要があります。 選択できるオプションは、 blocking
とtrue
2つです。
-
fallback: blocking
(推奨)
生成されていないページに対してリクエストが行われると、Next.jsは最初のリクエストでページをサーバーレンダリングします。 今後のリクエストでは、キャッシュから静的ファイルが提供されます。 -
fallback: true
生成されていないページに対してリクエストが行われると、Next.jsは、最初のリクエストで読み込み状態の静的ページをすぐに提供します。 データの読み込みが完了すると、ページは新しいデータで再レンダリングされ、キャッシュされます。 今後のリクエストでは、キャッシュから静的ファイルが提供されます。
// pages/products/[id].js export async function getStaticPaths() { const products = await getTop1000Products() const paths = products.map((product) => ({ params: { id: product.id } })) return { paths, fallback: 'blocking' } }
トレードオフ
Next.jsは、何よりもまずエンドユーザーに焦点を当てています。 「最良のソリューション」は相対的なものであり、業界、対象者、およびアプリケーションの性質によって異なります。 Next.jsを使用すると、開発者はフレームワークの境界を離れることなくソリューション間を移動できるため、プロジェクトに適したツールを選択できます。
サーバーサイドレンダリング
ISRが常に正しいソリューションであるとは限りません。 たとえば、Facebookのニュースフィードに古いコンテンツを表示することはできません。 この場合、コンテンツを無効にするために、SSRと、場合によっては代理キーを含む独自のcache-control
ヘッダーを使用する必要があります。 Next.jsはハイブリッドフレームワークであるため、そのトレードオフを自分で行い、フレームワーク内にとどまることができます。
// You can cache SSR pages at the edge using Next.js // inside both getServerSideProps and API Routes res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate');
SSRとエッジキャッシングはISRに似ています(特に、 stale-while-revalidate
キャッシングヘッダーを使用する場合)が、主な違いは最初のリクエストです。 ISRを使用すると、事前にレンダリングされた場合、最初のリクエストが静的であることが保証されます。 データベースがダウンした場合、またはAPIとの通信に問題が発生した場合でも、ユーザーには適切に提供された静的ページが表示されます。 ただし、SSRを使用すると、着信要求に基づいてページをカスタマイズできます。
注:キャッシュなしでSSRを使用すると、パフォーマンスが低下する可能性があります。 ユーザーがサイトを表示できないようにブロックする場合、ミリ秒ごとに問題が発生します。これは、TTFB(Time to First Byte)に劇的な影響を与える可能性があります。
静的サイトの生成
ISRは、小さなWebサイトでは必ずしも意味がありません。 再検証期間がサイト全体の再構築にかかる時間よりも長い場合は、従来の静的サイト生成を使用することをお勧めします。
クライアント側のレンダリング
Next.jsなしでReactを使用する場合は、クライアント側のレンダリングを使用しています。 アプリケーションは読み込み状態を提供し、続いてクライアント側のJavaScript内のデータを要求します(例: useEffect
)。 これにより、ホスティングのオプションが増えますが(サーバーは必要ないため)、トレードオフがあります。
最初のHTMLから事前にレンダリングされたコンテンツがないため、検索エンジン最適化(SEO)の速度が遅くなります。 また、JavaScriptを無効にしてCSRを使用することもできません。
ISRフォールバックオプション
データをすばやく取得できる場合は、 fallback: blocking
。 そうすれば、読み込み状態を考慮する必要がなくなり、ページには常に同じ結果が表示されます(キャッシュされているかどうかに関係なく)。 データのフェッチが遅い場合、 fallback: true
を使用すると、ユーザーに読み込み状態をすぐに表示できます。
ISR:キャッシングだけではありません!
ISRについてはキャッシュのコンテキストで説明しましたが、展開間で生成されたページを永続化するように設計されています。 これは、以前に生成されたページを失うことなく、即座にロールバックできることを意味します。
各デプロイメントは、Next.jsが静的に生成されたページを永続化するために使用するIDによってキー設定できます。 ロールバックするときに、キーを更新して前のデプロイメントを指すようにし、アトミックデプロイメントを可能にします。 これは、以前の不変のデプロイメントにアクセスでき、意図したとおりに機能することを意味します。
- ISRを使用してコードを元に戻す例を次に示します。
- コードをプッシュして、デプロイメントID123を取得します。
- あなたのページにはタイプミス「SmshngMagazine」が含まれています。
- CMSでページを更新します。 再デプロイは必要ありません。
- ページに「SmashingMagazine」と表示されると、ストレージに保持されます。
- いくつかの悪いコードをプッシュして、ID345をデプロイします。
- 展開ID123にロールバックします。
- まだ「SmashingMagazine」が表示されます。
静的ページの復帰と永続化はNext.jsの範囲外であり、ホスティングプロバイダーに依存します。 ISRは、設計上、キャッシュが期限切れになるため、 Cache-Control
ヘッダーを使用したサーバーレンダリングとは異なることに注意してください。 それらはリージョン間で共有されず、元に戻すときに削除されます。
インクリメンタル静的再生の例
インクリメンタルスタティックリジェネレーションは、eコマース、マーケティングページ、ブログ投稿、広告付きメディアなどに適しています。
- Eコマースデモ
Next.js Commerceは、高性能eコマースサイト向けのオールインワンスターターキットです。 - GitHubリアクションデモ
元のGitHubの問題に対応し、ISRが静的に生成されたランディングページを更新するのを確認します。 - 静的ツイートデモ
このプロジェクトは30秒でデプロイされますが、ISRを使用してオンデマンドで5億件のツイートを静的に生成できます。
Next.jsを今すぐ学ぶ
開発者と大規模なチームは、ハイブリッドアプローチとオンデマンドでページを段階的に生成する機能のためにNext.jsを選択しています。 ISRを使用すると、サーバーレンダリングの柔軟性を備えた静的な利点を得ることができます。 ISRは、nextstartを使用してそのまま動作しnext start
。
Next.jsは、段階的に採用されるように設計されています。 Next.jsを使用すると、既存のコードを引き続き使用して、必要なだけ(または少しだけ)Reactを追加できます。 小さく始めてページを段階的に追加することで、完全な書き換えを回避することで機能の動作を妨げることを防ぐことができます。 Next.jsの詳細をご覧ください—そして皆さん、幸せなコーディングを!
参考文献
- Next.js入門
- Next.jsでのスタイリング方法の比較
- Next.jsAPIルートを使用してGraphQLサーバーを構築する方法