App Router(Next.js 13.4.4)のSSGで作成したサイト を、 ISR にしたらどれぐらいパフォーマンスが変わるのかを比べてみようと思い、キャッシュ を調べながら、SSGからISRに切り替えてみました。
blog.kimizuka.org
nextjs.org
nextjs.org
DEMO(SSGバージョン)
DEMO(ISRバージョン)
SSGからISRへの変更手順
❶ next.config.js のoutputを削除
/** * @type {import('next').NextConfig} */ const nextConfig = { // output: 'export' // 削除 }; module.exports = nextConfig;
❷ APIディレクトリを作成
Pages Routerの頃とAPIの作成方法に変更があり、 pages/api/works/index.tsx ではなく app/api/works/route.ts という感じで route.ts を作成する形となっております。
app/api/works/route.ts
import { NextResponse } from 'next/server'; import { getWorks } from '@/scripts/getWorks'; // SSGの際に使っていた記事取得のスクリプト export async function GET(request: Request) { const works = await getWorks(); return NextResponse.json(works); }
❸ 記事の取得部分をfetchに変更
記事一覧の取得をAPI経由に変更します。
app/works/page.tsx
import { notFound } from 'next/navigation'; // import { getWorks } from '@/scripts/getWorks'; // 削除 import { nextRevalidate } from '@/scripts/variable'; import { WorksPageTemplate } from '@/components/templates/WorksPageTemplate'; export default async function WorksPage({ id: string; }; }) { // const contents = await getWorks(); // 削除 const response = await fetch( // 追加 `${ process.env.BASE_URL }/api/v1/works`, // 追加 { // 追加 next: { // 追加 revalidate: nextRevalidate // 追加 } // 追加 } // 追加 ); // 追加 if (!response.ok) { // 追加 return notFound(); // 追加 } // 追加 const { contents } = await response.json(); // 追加 return ( <WorksPageTemplate contents={ contents } /> ); }
ローカルでは、localhost:3000/api/v1/works から、Vercelにデプロイした後はプレビューURLからデータを取得したいので、URLは.envに記載します。また、キャッシュの有効期限も管理しやすいように別ファイル(scripts/variable.ts)にしておきます。
❸ buildの確認
ローカルでは、localhost:3000/api/v1/works からデータを取得するため、
- yarn dev でローカルサーバをたてる
- yarn build でビルドを実行
でビルドを実行します。
localhost:3000/api/v1/works からデータを取得できないとビルドがコケるからです。
❹ Vercelにデプロイ
- environment-variablesからPreviewのBASE_URLを設定する
- next.config.js と app/api 以下を先にデプロイする
- すべてをデプロイする
という順に実行します。
/api/v1/works からデータを取得できないとビルドがコケるからです。
ISRからSSGへの変更手順
ISR化の逆の手順を踏んでいきます。
❶ next.config.js のoutputを追加
/** * @type {import('next').NextConfig} */ const nextConfig = { output: 'export' // 追加 }; module.exports = nextConfig;
❷ APIディレクトリを削除
app/api か残っているとビルドがコケるので削除します。
❸ 記事の取得部分のfetchを削除
app/works/page.tsx
import { notFound } from 'next/navigation'; import { getWorks } from '@/scripts/getWorks'; // 追加 import { nextRevalidate } from '@/scripts/variable'; import { WorksPageTemplate } from '@/components/templates/WorksPageTemplate'; export default async function WorksPage({ id: string; }; }) { const contents = await getWorks(); // 追加 // const response = await fetch( // 削除 // `${ process.env.BASE_URL }/api/v1/works`, // 削除 // { // 削除 // next: { // 削除 // revalidate: nextRevalidate // 削除 // } // 削除 // } // 削除 // ); // 削除 // if (!response.ok) { // 削除 // return notFound(); // 削除 // } // 削除 // const { contents } = await response.json(); // 削除 return ( <WorksPageTemplate contents={ contents } /> ); }
❹ Vercelにデプロイ
- environment-variablesからPreviewのBASE_URLを削除する
- Vercelにデプロイする
という感じで、ISRとSSGを切り替えながら検証したのですが、てみました。結果として、 今回のサイト ではSSGを採用しました。
ISRは記事を投稿した瞬間にページにアクセスできるようになる魅力があるものの、記事の更新頻度が高くないので、記事を書いたタイミングでページを作成した方が効率が良いと考えたからです。記事を誰でも投稿できるサイト(WikiやBBSなど)で、API取得ではなくサーバサイドでレンダリングをかけたい場合はISRがよさそうです。