Next.js 16、キャッシュ コンポーネントと React コンパイラー: 実際に何が変わるのか
· 2 min read · Filippo Spinella · Next.js, React, Frontend, Performance
Next.js で長年悩まされてきた質問の 1 つは、「このページは静的ですか、それとも動的ですか?」というものでした。 cookies()、さまざまなオプションを備えた fetch、データベース クライアント、CMS、ショッピング カート、またはカスタム コンテンツへの呼び出しを追加するまでは、これは単純な質問のように思えます。
Next.js 16 は、この会話の謎を少なくしようとしている点で興味深いです。複雑さが解消されるわけではありませんが、メンタル モデルが変わります。ルートはデフォルトで動的であり、キャッシュは必要に応じて自身を宣言し、Suspense は新鮮なままの部分で高速シェルを構成する自然な方法になります。
理解すべき機能はキャッシュ コンポーネントです。安定した Turbopack、React Compiler、proxy.ts、および新しい無効化 API は重要ですが、それらは同じ問題、つまりフレームワークが舞台裏で何を決定したかを推測することなく高速アプリを構築することを中心に展開しています。
このことは重要だから
実際のアプリには、「静的ページ」と「動的ページ」だけが存在するわけではありません。さまざまなニーズを持つさまざまな部品があります。
商品シートは一日に数回変更される場合がございます。価格はより頻繁に変更される可能性があります。可用性はほぼライブである必要があります。ユーザー名は個人的なものです。レビューはストリーミングできます。サイドバーを安定させることができます。カートにはありません。
すべてを 1 つの単位として扱うと、常に次の 2 つの極端などちらかになります。
- 積極的なキャッシュと古いデータが表示されるリスク。
- あらゆる場所で動的レンダリングが行われ、必要以上にパフォーマンスが低下します。
キャッシュ コンポーネントは、まさにこの誤った選択を回避するために機能します。
実際のモデル
cacheComponents: true を使用すると、"use cache" を使用してキャッシュ可能なものを宣言できます。次に、期間とタグを cacheLife() および cacheTag() に関連付けることができます。動的部分は動的のままであり、Suspense を使用して分離できます。
セットアップは小規模です:
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { cacheComponents: true, }; export default nextConfig;
大きな変更は設定にありません。それはコンポーネントの記述を開始する方法にあります。
// app/products/[slug]/page.tsx import { Suspense } from 'react'; import { cacheLife, cacheTag } from 'next/cache'; async function getProduct(slug: string) { 'use cache'; cacheLife('hours'); cacheTag(`product:${slug}`); return db.product.findUnique({ where: { slug } }); } async function ProductDetails({ slug }: { slug: string }) { const product = await getProduct(slug); return ( <section> <h1>{product.name}</h1> <p>{product.description}</p> </section> ); } async function LiveInventory({ slug }: { slug: string }) { const inventory = await db.inventory.findFirst({ where: { slug } }); return <p>{inventory.quantity} pezzi disponibili</p>; } export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params; return ( <> <ProductDetails slug={slug} /> <Suspense fallback={<p>Controllo disponibilità...</p>}> <LiveInventory slug={slug} /> </Suspense> </> ); }
ページはすべてキャッシュされている必要も、すべてが動的である必要もありません。製品シートは迅速かつ再利用可能です。在庫は常に新鮮な状態を保つことができます。ユーザーは、最も遅い部分を待たずに、すぐに何かを確認できます。
use cache は実行可能なドキュメントです
"use cache" について私が気に入っている点は、意図を明示的にするよう強制することです。関数を読むと、誰かが「このデータは再利用できる」と判断したことがすぐにわかります。
これは、fetch を使用していない場合に特に便利です。多くのアプリは、Prisma、Drizzle、内部 SDK、CMS クライアント、またはサービス機能からデータを読み取ります。このような場合、fetch オプションのみに基づく従来の推論では十分ではありませんでした。
経験則:
- カシェアの内容は比較的安定しています。
- 詳細なタグを使用します。
- 動的な権限、セッション、カート、通知、トランザクション状態を残します。
- 遅い部分を
Suspense内に配置します。 - 「パフォーマンスが向上した」と言う前に、測定してください。
すべてを捨てずに無効化する
キャッシュは、正確に更新できる場合にのみ役立ちます。ここで、cacheTag、revalidateTag、updateTag が重要になります。
例:
'use server'; import { updateTag } from 'next/cache'; export async function updateProductName(productId: string, name: string) { await db.product.update({ where: { id: productId }, data: { name }, }); updateTag(`product:${productId}`); }
重要なディテールはタグです。 product:${productId} は正確な境界を示します。 products は巨大なバケットを示します。巨大なバケツは最初は快適です。数か月後には、タイトルを変更するためにアプリの半分を無効にする理由になります。
Stable Turbopack: 毎日耳にする部分
Next.js 16 では、開発とビルドの中心に Turbopack が組み込まれます。これは最も詩的な機能ではありませんが、作業中に感じるものです。サーバーの起動が早くなり、リフレッシュが速くなり、強制的なコーヒー ブレークのように感じなくなるビルドです。
とはいえ、カスタム プラグインが満載のコードベースを目をつぶって移行するつもりはありません。私ならチェックします:
- ローカルビルド。
- 非標準輸入。
- MDX、SVG、CSS。
- Webpack プラグインは残っています。
- 重要なページ。
- ビルド時間の違い。
新しいプロジェクトの場合は、デフォルトから開始します。成熟したものについては、慎重な移行を行うと思います。
React Compiler: 思考ではなくノイズを除去する
React Compiler 1.0 は安定しており、Next.js 16 は reactCompiler でそれをサポートしています。約束は、手動によるメモ化を大幅に減らすことです。つまり、「安全のため」に使用される memo、useMemo、useCallback が減ります。
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { reactCompiler: true, }; export default nextConfig;
私はそれを魔法の粉のように扱うつもりはありません。コンパイラは、コードが React ルールに適切に準拠している場合に役立ちます。コンポーネントに奇妙な副作用、隠れた突然変異、不適切に使用されたフックがある場合は、まずそれを修正する必要があります。
健康的に試してみる方法:
eslint-plugin-react-hooksを更新します。- 実際の違反を修正する。
- 管理されたエリアでそれを有効にします。
- ビルド時間と動作を測定します。
- 手動メモ化は、不要になった場合にのみ削除してください。
目標は、すべての useMemo を消去することではありません。目標は、レンダリングが怖いという理由で予防的なメモ化を書くのをやめることです。
proxy.ts とネットワーク境界
古い middleware.ts は proxy.ts になります。これは名前の変更ですが、これには意味があります。そのファイルはリクエスト境界に位置し、従来のバックエンド スタイルの汎用ミドルウェアではありません。
// proxy.ts import { NextRequest, NextResponse } from 'next/server'; export default function proxy(request: NextRequest) { const isLoggedIn = Boolean(request.cookies.get('session')); if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); }
ここでのルールは単純です。小さく保つことです。リダイレクト、認証ルーティング、ヘッダー、重要な書き換え。 2 つ目のバックエンドのように感じ始めたら、おそらくやりすぎです。
実際にどうやって移住するか
すべての機能を一度にオンにすることはありません。私だったらこうします:
- Next、React、React DOM を更新します。
- 公式 codemod を起動します。
params、searchParams、cookies()、headers()、draftMode()の重大な変更を修正しました。middleware.tsをproxy.tsに移行します。- 私はビルドと重要なページをチェックします。
- 現在キャッシュによって摩擦が生じているセクションでキャッシュ コンポーネントを有効にします。
- 私はタグと無効化の規則を定義します。
- React Compiler を個別に試してみます。
- 前後のメトリクスの比較。
優れた移行とは、すべての新機能を使用するものではありません。これにより、アプリの動作が読みやすくなります。
考え方で何が変わるのか
Next.js 16 の最も便利な点は、インテンションに適切な名前を付けられることです。機能は単に「データベースから商品を取得する」だけではありません。それは、「商品を入手し、何時間でもキャッシュできます。このタグで無効にします」です。コンポーネントは単に「ページをレンダリングする」だけではありません。それは、「これは高速シェルであり、この作品は個人的なものであり、これはストリーミングで提供されます。」です。
最初はもっと仕事が必要なように思えます。そうすれば、それは穏やかな形になります。パフォーマンスの決定は、デフォルト、ヒューリスティック、および部族の記憶の組み合わせに隠れることはなくなりました。それらはコードの中にあります。