3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js】4種類の"Cache"を理解したい Part ③ Full Route Cache

Last updated at Posted at 2024-04-16

App Routerの登場で、より強調されたCachingについて理解を深めたいと思います。

ドキュメントに沿って、4回に分けて記事にしていきます。

  1. Request Memoization
  2. Data Cache
  3. Full Route Cache
  4. Router Cache

今回はFull Route Cacheです。

V14までの仕様です。
V15からは、Data CacheRouter Cacheがデフォルトで無効になります。
詳細はこちら

概要

Next.jsは、**ビルド/Revalidation時に、静的レンダリングするRouteに対して、**自動でReact Server Component(RSC) PayloadとHTMLをRenderingし、Cacheしてくれます。

このプロセスはいわゆる静的サイト生成や静的レンダリングを指します。

これにより、Client Sideからのリクエスト時にRenderingを行うのではなく、事前に生成されたRSC Payload、HTMLを利用することで、素早いページロードを実現します。

Server Side Rendering プロセス

以下のプロセスの成果物であるRSC PayloadHTMLがFull Route Cacheに入ってきます。

  1. ReactがServer ComponentをRSC Payloadフォーマットに変換
  2. Next.jsがRSC PayloadとClient ComponentのJavaScriptを用いて、HTMLを生成

How it works

image.png
出典 : 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からのリクエストで使用されます。

以下のプロセスにより、完全なページとして動作します。

  1. HTMLが初期表示データとして使用される
    ※この時点では、あくまでUIプレビューとして表示され、イベントなどの処理は紐づけられていません。
  2. RSC Payloadを用いて、DOMを再調整する
  3. Hydrateが行われ、イベント紐づけされ、インタラクティブな状態になる

保持期間

永続的に保持されます。

Revalidating

2種類の方法があります。

Cache無効化

以下のいずれかの方法で無効化できます。

export const dynamic = 'force-dynamic'
// Or
export const revalidate = 0

実際に確認

.next/server/appフォルダに対して、.rsc.htmlの生成有無を確認していきます。

実装

Project
shell
npx create-next-app@latest --typescript --tailwind
Component
src/app/full-route-cache/page.tsx
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>;
}
src/app/full-route-cache/data-cache-revalidating/page.tsx
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>
  );
}
src/components/full-route-cache/revalidate-path.tsx
'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>;
}
src/app/full-route-cache/dynamic-function/page.tsx
import { cookies } from 'next/headers';

export default async function DynamicFunction() {
  cookies();
  return <div />;
}
src/app/full-route-cache/route-segment/page.tsx
export const dynamic = 'force-dynamic';

export default async function RouteSegment() {
  return <div />;
}
src/app/full-route-cache/data-cache-opting-out/page.tsx
import { getRandomNumberWithNoDataCache } from '@/utils/app-fetch';

export default async function DataCacheOptingOut() {
  await getRandomNumberWithNoDataCache();
  return <div />;
}
Route Handler
src/app/api/full-route-cache/revalidate-path/route.ts
import { revalidatePath } from 'next/cache';

export async function GET() {
  revalidatePath('/full-route-cache/data-cache-revalidating');
  return new Response('', { status: 200 });
}
Fetch Function
src/utils/app-fetch.ts
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サーバーを起動

shell
node external-server.js
shell
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には以下のような値が静的に生成されています。

.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には以下のような値が静的に生成されています。

.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してみましょう。

image.png

すると、以下のように値が変わっていることが確認できますね!

つまり、Full Route Cacheされていた内容が更新されたということになります。

.next/server/app/full-route-cache/data-cache-revalidating.html
 <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の良さを消してしまうことになりそうです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?