App Routerの登場で、より強調されたCachingについて理解を深めたいと思います。
ドキュメントに沿って、4回に分けて記事にしていきます。
- Request Memoization
- Data Cache
- Full Route Cache
- Router Cache
今回はFull Route Cacheです。
V14までの仕様です。
V15からは、Data CacheとRouter Cacheがデフォルトで無効になります。
詳細はこちら
概要
Next.jsは、**ビルド/Revalidation時に、静的レンダリングするRouteに対して、**自動でReact Server Component(RSC) PayloadとHTMLをRenderingし、Cacheしてくれます。
このプロセスはいわゆる静的サイト生成や静的レンダリングを指します。
これにより、Client Sideからのリクエスト時にRenderingを行うのではなく、事前に生成されたRSC Payload、HTMLを利用することで、素早いページロードを実現します。
Server Side Rendering プロセス
以下のプロセスの成果物であるRSC Payload
とHTML
がFull Route Cacheに入ってきます。
- ReactがServer ComponentをRSC Payloadフォーマットに変換
- Next.jsがRSC PayloadとClient ComponentのJavaScriptを用いて、HTMLを生成
How it works
出典 : How the Full Route Cache Works
ビルド/Revalidation時にRSC PayloadとHTMLをRendaringし、Cacheしています。
※MISS
が入っていますが、Cacheが生成されるビルド/Revalidationのタイミングでは常にMISS
になると理解しています。Full Route CacheにHIT
するのは、Client Sideからページリクエストが来たタイミングとなるはずです。
Client Side Rendering プロセス
Full Route Cacheは、Client Sideからのリクエストで使用されます。
以下のプロセスにより、完全なページとして動作します。
- HTMLが初期表示データとして使用される
※この時点では、あくまでUIプレビューとして表示され、イベントなどの処理は紐づけられていません。 - RSC Payloadを用いて、DOMを再調整する
-
Hydrate
が行われ、イベント紐づけされ、インタラクティブな状態になる
保持期間
永続的に保持されます。
Revalidating
2種類の方法があります。
- Data Cache Revalidating
- Build
Cache無効化
以下のいずれかの方法で無効化できます。
export const dynamic = 'force-dynamic'
// Or
export const revalidate = 0
実際に確認
.next/server/app
フォルダに対して、.rsc
と.html
の生成有無を確認していきます。
実装
Project
npx create-next-app@latest --typescript --tailwind
Component
import { getRandomNum } from '@/utils/app-fetch';
export default async function Home() {
const val = await getRandomNum();
console.log('Full Route Cache', val);
return <div>{val}</div>;
}
import RevalidatePath from '@/components/full-route-cache/revalidate-path';
import { getRandomNum } from '@/utils/app-fetch';
export default async function DataCacheRevalidating() {
const val = await getRandomNum();
console.log('DataCacheRevalidating', val);
return (
<div>
{val}
<br />
<RevalidatePath />
</div>
);
}
'use client';
import { revalidatePath } from 'next/cache';
export default function RevalidatePath() {
const onClick = async () => {
await fetch('/api/full-route-cache/revalidate-path');
};
return <button onClick={onClick}>Revalidate</button>;
}
import { cookies } from 'next/headers';
export default async function DynamicFunction() {
cookies();
return <div />;
}
export const dynamic = 'force-dynamic';
export default async function RouteSegment() {
return <div />;
}
import { getRandomNumberWithNoDataCache } from '@/utils/app-fetch';
export default async function DataCacheOptingOut() {
await getRandomNumberWithNoDataCache();
return <div />;
}
Route Handler
import { revalidatePath } from 'next/cache';
export async function GET() {
revalidatePath('/full-route-cache/data-cache-revalidating');
return new Response('', { status: 200 });
}
Fetch Function
export async function getRandomNum() {
const res = await fetch('http://localhost:3005');
return await res.text();
}
// Opt-out data cache
export async function getRandomNumberWithNoDataCache() {
const res = await fetch('http://localhost:3005', { cache: 'no-store' });
console.log(await res.text());
}
ダミー外部APIサーバー
こちらと同様にextenral-server.js
ファイルを作成してください。
外部API、Next.Jsサーバーを起動
node external-server.js
npm run build && start
Full Route Cacheは生成されている?
Cacheが生成される場合
/full-route-cache
はシンプルにCacheが生成されます。
.next/server/app/full-route-cache.html
と.next/server/app/full-route-cache.rsc
が確認できますね!
更に、Build時に出力されるログを確認します。
Full Route Cache 784
と出力されていた場合、.next/server/app/full-route-cache.html
には以下のような値が静的に生成されています。
<body class="__className_aaf875">
<!--$-->
<div>784</div>
<!--/$-->
Cache Revalidating
/full-route-cache/data-cache-revalidating
では、revalidatePath()
を発火後にCacheが再構築されていることを確認します。
.next/server/app/full-route-cache/data-cache-revalidating.html
と.next/server/app/full-route-cache/data-cache-revalidating.rsc
が生成されています。
更に、Build時に出力されるログを確認します。
DataCacheRevalidating 784
と出力されていた場合、.next/server/app/full-route-cache/data-cache-revalidating.html
には以下のような値が静的に生成されています。
<body class="__className_aaf875">
<!--$-->
<div>784</div>
<!--/$-->
http://localhost:3000/full-route-cache/data-cache-revalidating
にアクセスすると、以下のようなUIが描画されるはずです。
Revalidate
をクリックし、Reloadしてみましょう。
すると、以下のように値が変わっていることが確認できますね!
つまり、Full Route Cacheされていた内容が更新されたということになります。
<body class="__className_aaf875">
<!--$-->
<div>523</div>
<!--/$-->
Cacheを無効化
/full-route-cache/dynamic-function
、/full-route-cache/route-segment
、/full-route-cache/data-cache-opting-out
には、Opting Outする処理が組み込まれており、.next/server/app/full-route-cache
配下に.html
と.rsc
が生成されていないことが分かりますね!
最後に
シンプルに静的生成されるRouteはFull Route Cacheされるという感覚でOKだと思います!
ただ、revalidatePath()
などでCacheが再更新されてしまうので、静的生成したRouteに関しては特別な管理をしないとFull Route Cacheの良さを消してしまうことになりそうです。