Next.jsのIncremental Static Regenerationが凄い
こんにちは!
リクパー フロントエンドエンジニアの谷本です。
早いもので入社して2ヶ月半が経ち、家の近くでは桜が咲き始めました。
今回は、現在開発中のプロジェクトで使用しているNext.jsについて、知らないことばかりで勉強になったので、簡単にまとめてみようと思います。
Next.jsとは
Next.jsとは、Reactの機能を拡張するためのJavaScriptフレームワークです。
Next.jsは、2016年にVercel社によりバージョン1.0がリリースされ、現在も開発が継続されています。
Next.jsの特徴
SSR(Server Side Rendering)とSSG(Static Site Generator)
ReactはSPA(Single Page Application)として単一の巨大なJavaScriptを生成します。
SPAはブラウザによるページ遷移を行わずにコンテンツの切り替えなどを行うことで、ユーザー体験(UX)を大きく向上させることができますが、今までサーバー側で行っていたHTML生成をブラウザ側で行うことになるため、初期ローディングの時間がかかったり、通常のWebページと比べSEOの面で不利になるというデメリットがあります。
それに対して、Next.jsはアプリケーションを事前にページ単位でレンダリングします。
クライアント側からのリクエスト時にレンダリングするのがSSR(Server Side Rendering)と呼ばれる機能です。また2020年3月リリースのv9.3より、ビルド時にレンダリングするSSG(Static Site Generation)と呼ばれる機能も追加されました。
これらの機能により、各ページ読み込み時のダウンロードファイルサイズを削減できます。またURLごとに個別のHTMLが生成されるので、SEOに有利です。
ちなみに、SSRとSSGの切り替えはビルド時にNext.jsが自動的に判断します。getServerSidePropsなどSSR用のメソッドが使われていればビルド時には静的ファイルを作成しません。反対に、それらが使われていなければビルド時に自動で静的ファイルの生成までやってくれます。(素晴らしい・・・)
SSR,SSGについては松永さんのnoteでも詳しくお話されています。
Next.jsはSSRのためのフレームワークではない
Next.jsはSSRのためのフレームワークという印象がありましたが、現在(v10.0.9)、下記のとおりStatic Site Generation(SSG)が推奨されています。
We recommend using Static Generation over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN with no extra configuration to boost performance.
Basic Features: Pages | Next.js
SSGは、ビルド時にすべてのページを生成するため、リクエストごとにページ生成するSSRよりも高速です。
Next.jsはこのパフォーマンスをウリにしていて、「どや、ウチの速いやろ・・・ええからSSG使ってみ」と言ってるわけなんですね。
ISG(Incremental Static Generation)
SSGでは、getStaticProps関数により、ビルド時にデータを取得してページにデータを渡すことができます。
export async function getStaticProps(context) {
return {
props: { 渡したいデータ },
}
}
また、getStaticPaths関数により、一覧ページ→詳細ページのような動的な(臨機応変な、柔軟な)ページ遷移にも対応しています。
export async function getStaticProps(context) {
const postId = context.params.id // context.paramsによりpathsで指定したparamsを参照
const post = postIDによるpostデータ取得処理
return {
props: { postId, post },
}
}
export async function getStaticPaths() {
return {
paths: [ // 生成されるページのURLのパス一覧
{ params: { id: 'my-first-post' } },
{ params: { id: 'my-second-post' } },
{ params: { id: 'my-third-post' } },
],
fallback: false
}
}
上記コードでは、https://example.com/posts/選んだidにアクセスすると、idに対応したデータ内容が表示されたページを取得することができます。
getStaticPaths関数には必ずfallbackキーを記述します。
pathsにないパスに対してリクエストがあったときに、fallbackキーの値によって挙動の違いがあります。
false:404ページを返す
true:まずはデータ取得が必要な部分以外を返してからデータ取得が行われる
blocking:”データ取得が必要な部分以外を返す”という動作をブロックして(=行わず)、データ取得が終わってからページ全体を返す(v10より追加)
getStaticPaths関数の例のように、段階的に(クライアント側からのリクエストにより)ページが静的に生成される機能の総称をISG(Incremental Static Generation)といいます。
SSGの問題点
SSGは前述のとおりビルド時にデータを取得してページに表示します。
ここで問題が発生。
ビルド以降、データが更新されたらページに反映されない。(例:一覧データが増えても反映されない)
外部データの取得をクライアント側で行うと、結局CSRによって成り立っているのと同じ。
なるべくSSGを使いたいが、規模が大きくなるほど使い勝手が悪くなる・・
そこでv9.4より登場したのがISRです!
ISR(Incremental Static Regeneration)
直訳すると"段階的静的再生成"とあるように、
ISRはクライアント側のリクエストに対しビルド時に生成された静的ページを返し、かつバックグラウンドで一定期間ごとに静的ページの再生成をサーバー側で行います。
これにより、SSGの問題点に対応できます!
getStaticProps関数にrevalidateを追加
具体的にコードで見ます。
export async function getStaticProps(context) {
const postId = context.params.id
const post = postIDによるpostデータ取得処理
return {
props: { postId, post },
revalidate: 30, // ここを追加
}
}
getStaticProps 関数から return するオブジェクトの中で、revalidate というキーに対して秒数を指定します。ここでは30秒を指定しています。
つまり30秒経過後クライアント側からリクエストがあったら、クライアントには既に生成されたページを見せつつ、バックグラウンドでデータの再取得及び再レンダリングを行いページを再生成し、次のリクエストに対しては再生成されたページを返します。
これによって、データの変化も反映することができます。
revalidateで秒数を指定するだけ。凄い!
しかし、Next.jsについて予備知識がなさすぎて、凄い!までたどり着くのに時間がかかりました。
さいごに
リクパーでは少人数チームということもあり、私のような新人でも技術選定の段階から開発に携わることができます。
実装したい機能をふまえ、どの技術を使うべきか?
そもそもフレームワークやライブラリについて知らないものも多く、その中から何を選択するのが最適か、試行錯誤の日々です。
Next.jsのようにフロントエンドの最新技術を使って開発できることは、とてもありがたいですし、勉強になります!
自己の成長が会社の成長に繋がる、日々その責任とやりがいを感じながら開発に臨んでいます。
それでは、また!