spinny:~/writing $ vim nextjs-16-cache-components.md
1~2В течение многих лет одним из самых раздражающих вопросов в Next.js был: «Эта страница статичная или динамическая?». Это кажется простым вопросом, пока вы не добавите вызов `cookies()`, `fetch` с различными опциями, клиента базы данных, CMS, корзины покупок или фрагмента пользовательского контента.3~4Next.js 16 интересен тем, что пытается сделать этот разговор менее загадочным. Это не устраняет сложность, но меняет ментальную модель: маршруты по умолчанию являются динамическими, кэш объявляет себя там, где это необходимо, а `Suspense` становится естественным способом составления быстрых оболочек из частей, которые остаются свежими.5~6Особенность, которую нужно понять, — это компоненты кэша. Стабильный Turbopack, React Compiler, `proxy.ts` и новые API-интерфейсы инвалидации важны, но они вращаются вокруг одной и той же проблемы: создание быстрых приложений без необходимости угадывать, что за кулисами решила платформа.7~8## Потому что это важно9~10В реальном приложении есть не просто «статические страницы» и «динамические страницы». У вас есть разные вещи с разными потребностями.11~12Список продуктов может меняться несколько раз в день. Цена может меняться чаще. Наличие должно быть почти живое. Имя пользователя является личным. Обзоры можно транслировать. Боковая панель может быть стабильной. Тележки нет.13~14Если вы относитесь ко всему как к единому целому, вы всегда попадаете в одну из двух крайностей:15~16- агрессивное кэширование и риск увидеть старые данные;17- динамический рендеринг везде и производительность хуже, чем нужно.18~19Компоненты кэша служат именно для того, чтобы избежать этого ошибочного выбора.20~21## Модель на практике22~23С помощью `cacheComponents: true` вы можете объявить, что кэшируется, используя `"use cache"`. Затем вы можете связать продолжительность и теги с `cacheLife()` и `cacheTag()`. Динамические части остаются динамическими и могут быть изолированы с помощью `Suspense`.24~25```mermaid26flowchart TD27 Request[Запрос пользователя] --> Shell[Кэшированная оболочка]28 Request --> Dynamic[Динамические разделы]29 Shell --> FirstPaint[Первый быстрый контент]30 Dynamic --> Stream[Стриминг внутри саспенса]31 Stream --> Complete[Полная страница]32```33~34Установка небольшая:35~36```typescript37// next.config.ts38import type { NextConfig } from 'next';39~40const nextConfig: NextConfig = {41 cacheComponents: true,42};43~44export default nextConfig;45```46~47Большие изменения не в конфигурации. Дело в том, как вы начинаете писать компоненты.48~49```tsx50// app/products/[slug]/page.tsx51import { Suspense } from 'react';52import { cacheLife, cacheTag } from 'next/cache';53~54async function getProduct(slug: string) {55 'use cache';56~57 cacheLife('hours');58 cacheTag(`product:${slug}`);59~60 return db.product.findUnique({ where: { slug } });61}62~63async function ProductDetails({ slug }: { slug: string }) {64 const product = await getProduct(slug);65~66 return (67 <section>68 <h1>{product.name}</h1>69 <p>{product.description}</p>70 </section>71 );72}73~74async function LiveInventory({ slug }: { slug: string }) {75 const inventory = await db.inventory.findFirst({ where: { slug } });76 return <p>{inventory.quantity} pezzi disponibili</p>;77}78~79export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {80 const { slug } = await params;81~82 return (83 <>84 <ProductDetails slug={slug} />85 <Suspense fallback={<p>Controllo disponibilità...</p>}>86 <LiveInventory slug={slug} />87 </Suspense>88 </>89 );90}91```92~93Страница не обязательно должна быть полностью кэшированной или полностью динамической. Лист продукта может быть быстрым и многоразовым. Инвентарь может оставаться свежим. Пользователь видит что-то сразу, не дожидаясь самой медленной части.94~95## `use cache` — исполняемая документация.96~97Что мне нравится в `"use cache"`, так это то, что он заставляет вас явно выражать намерение. Когда читаешь функцию, сразу понимаешь, что кто-то решил: «эти данные можно использовать повторно».98~99Это особенно полезно, когда вы не используете `fetch`. Многие приложения считывают данные из Prisma, Drizzle, внутренних SDK, клиентов CMS или сервисных функций. В таких случаях старых рассуждений, основанных только на опциях `fetch`, было недостаточно.100~101Эмпирическое правило:102~103- содержимое кэша относительно стабильное;104- используйте детальные теги;105- оставляет динамические разрешения, сеансы, корзины, уведомления и состояния транзакций;106- поместите медленные части внутрь `Suspense`;107- измеряйте, прежде чем сказать: «Мы улучшили производительность».108~109## Недействителен, не выбрасывая все110~111Кэш полезен только в том случае, если вы можете его точно обновить. Здесь становятся важными `cacheTag`, `revalidateTag` и `updateTag`.112~113Пример:114~115```typescript116'use server';117~118import { updateTag } from 'next/cache';119~120export async function updateProductName(productId: string, name: string) {121 await db.product.update({122 where: { id: productId },123 data: { name },124 });125~126 updateTag(`product:${productId}`);127}128```129~130Важная деталь – бирка. `product:${productId}` указывает точную границу. `products` сообщает об огромном ведре. Поначалу огромное ведро удобно; через несколько месяцев это становится причиной того, что вы аннулируете половину приложения, чтобы изменить название.131~132## Стабильный турбопак: то, что вы слышите каждый день133~134Next.js 16 превращает Turbopack в центр разработки и сборки. Это не самая поэтичная особенность, но именно ее вы ощущаете во время работы: сервер запускается раньше, более быстрое обновление, сборки, которые перестают напоминать вынужденный перерыв на кофе.135~136Тем не менее, я бы не стал переносить кодовую базу, полную пользовательских плагинов, с закрытыми глазами. Я бы проверил:137~138- локальная сборка;139- нестандартный импорт;140- MDX, SVG и CSS;141- Остались плагины Webpack;142- критические страницы;143- различия во времени сборки.144~145Для новых проектов я бы начал со значения по умолчанию. Для зрелых я бы сделал размеренную миграцию.146~147## React Compiler: убираем шум, а не мысли148~149React Compiler 1.0 стабилен, а Next.js 16 поддерживает его с помощью `reactCompiler`. Обещание состоит в том, чтобы сократить объем ручного запоминания: меньше `memo`, меньше `useMemo`, меньше `useCallback`, используемых «в целях безопасности».150~151```typescript152// next.config.ts153import type { NextConfig } from 'next';154~155const nextConfig: NextConfig = {156 reactCompiler: true,157};158~159export default nextConfig;160```161~162Я бы не стал относиться к этому как к волшебной пыли. Компилятор помогает, когда код хорошо следует правилам React. Если компоненты имеют странные побочные эффекты, скрытые мутации или неправильно используемые хуки, это необходимо сначала исправить.163~164Здоровый способ попробовать:165~1661. обновить `eslint-plugin-react-hooks`;1672. фиксировать фактические нарушения;1683. включить его на контролируемой территории;1694. измерять время сборки и поведение;1705. удаляйте ручное запоминание только тогда, когда оно больше не нужно.171~172Цель состоит не в том, чтобы стереть все `useMemo`. Цель — прекратить писать профилактическую мемоизацию, потому что мы боимся рендеринга.173~174## `proxy.ts` и граница сети175~176Старый `middleware.ts` становится `proxy.ts`. Это изменение имени, но оно имеет смысл: этот файл находится на границе запроса, а не традиционное универсальное промежуточное программное обеспечение в стиле серверной части.177~178```typescript179// proxy.ts180import { NextRequest, NextResponse } from 'next/server';181~182export default function proxy(request: NextRequest) {183 const isLoggedIn = Boolean(request.cookies.get('session'));184~185 if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {186 return NextResponse.redirect(new URL('/login', request.url));187 }188~189 return NextResponse.next();190}191```192~193Правило здесь простое: пусть оно будет небольшим. Перенаправление, маршрутизация аутентификации, заголовки, существенные перезаписи. Если он начинает ощущаться как второй бэкэнд, возможно, он делает слишком много.194~195## Как бы я на самом деле мигрировал196~197Я бы не стал включать все функции сразу. Я бы сделал это:198~1991. Я обновляю Next, React и React DOM;2002. Запускаю официальный кодмод;2013. Я исправляю критические изменения в `params`, `searchParams`, `cookies()`, `headers()` и `draftMode()`;2024. Я переношу `middleware.ts` на `proxy.ts`;2035. Проверяю сборки и критические страницы;2046. Я включаю компоненты кэша в разделе, где кеш в настоящее время создает проблемы;2057. Я определяю соглашения для тегов и аннулирования;2068. Я пробую React Compiler отдельно;2079. сравнение показателей до и после.208~209Хорошая миграция — это не та миграция, которая использует все новые функции. Это делает поведение приложения более читабельным.210~211## Что меняется в образе мышления212~213Самое полезное в Next.js 16 — это то, что он заставляет вас лучше называть намерения. Функция — это не просто «получить товар из базы данных». Это «получите товар, я могу его кэшировать часами, я аннулирую его с помощью этого тега». Компонент — это не просто «рендеринг страницы». Это «это быстрая оболочка, это личное, это идет в потоковом режиме».214~215На первый взгляд кажется, что работы больше. Тогда это становится формой спокойствия. Решения по производительности больше не скрыты в сочетании настроек по умолчанию, эвристики и племенной памяти. Они в коде.216~217## Полезные источники218~219- [Примечания к выпуску Next.js 16](https://nextjs.org/blog/next-16)220- [Компоненты кэша — документация Next.js](https://nextjs.org/docs/app/getting-started/cache-components)221- [использовать кеш - документация Next.js](https://nextjs.org/docs/app/api-reference/directives/use-cache)222- [Реагирующий компилятор v1.0](https://react.dev/blog/2025/10/07/react-compiler-1)223~
NORMAL · nextjs-16-cache-components.md [readonly]223 lines · :q to close