spinny:~/writing $ less nextjs-16-cache-components.md
12В течение многих лет одним из самых раздражающих вопросов в Next.js был: «Эта страница статичная или динамическая?». Это кажется простым вопросом, пока вы не добавите вызов `cookies()`, `fetch` с различными опциями, клиента базы данных, CMS, корзины покупок или фрагмента пользовательского контента.34Next.js 16 интересен тем, что пытается сделать этот разговор менее загадочным. Это не устраняет сложность, но меняет ментальную модель: маршруты по умолчанию являются динамическими, кэш объявляет себя там, где это необходимо, а `Suspense` становится естественным способом составления быстрых оболочек из частей, которые остаются свежими.56Особенность, которую нужно понять, — это компоненты кэша. Стабильный Turbopack, React Compiler, `proxy.ts` и новые API-интерфейсы инвалидации важны, но они вращаются вокруг одной и той же проблемы: создание быстрых приложений без необходимости угадывать, что за кулисами решила платформа.78## Потому что это важно910В реальном приложении есть не просто «статические страницы» и «динамические страницы». У вас есть разные вещи с разными потребностями.1112Список продуктов может меняться несколько раз в день. Цена может меняться чаще. Наличие должно быть почти живое. Имя пользователя является личным. Обзоры можно транслировать. Боковая панель может быть стабильной. Тележки нет.1314Если вы относитесь ко всему как к единому целому, вы всегда попадаете в одну из двух крайностей:1516- агрессивное кэширование и риск увидеть старые данные;17- динамический рендеринг везде и производительность хуже, чем нужно.1819Компоненты кэша служат именно для того, чтобы избежать этого ошибочного выбора.2021## Модель на практике2223С помощью `cacheComponents: true` вы можете объявить, что кэшируется, используя `"use cache"`. Затем вы можете связать продолжительность и теги с `cacheLife()` и `cacheTag()`. Динамические части остаются динамическими и могут быть изолированы с помощью `Suspense`.2425```mermaid26flowchart TD27 Request[Запрос пользователя] --> Shell[Кэшированная оболочка]28 Request --> Dynamic[Динамические разделы]29 Shell --> FirstPaint[Первый быстрый контент]30 Dynamic --> Stream[Стриминг внутри саспенса]31 Stream --> Complete[Полная страница]32```3334Установка небольшая:3536```typescript37// next.config.ts38import type { NextConfig } from 'next';3940const nextConfig: NextConfig = {41 cacheComponents: true,42};4344export default nextConfig;45```4647Большие изменения не в конфигурации. Дело в том, как вы начинаете писать компоненты.4849```tsx50// app/products/[slug]/page.tsx51import { Suspense } from 'react';52import { cacheLife, cacheTag } from 'next/cache';5354async function getProduct(slug: string) {55 'use cache';5657 cacheLife('hours');58 cacheTag(`product:${slug}`);5960 return db.product.findUnique({ where: { slug } });61}6263async function ProductDetails({ slug }: { slug: string }) {64 const product = await getProduct(slug);6566 return (67 <section>68 <h1>{product.name}</h1>69 <p>{product.description}</p>70 </section>71 );72}7374async function LiveInventory({ slug }: { slug: string }) {75 const inventory = await db.inventory.findFirst({ where: { slug } });76 return <p>{inventory.quantity} pezzi disponibili</p>;77}7879export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {80 const { slug } = await params;8182 return (83 <>84 <ProductDetails slug={slug} />85 <Suspense fallback={<p>Controllo disponibilità...</p>}>86 <LiveInventory slug={slug} />87 </Suspense>88 </>89 );90}91```9293Страница не обязательно должна быть полностью кэшированной или полностью динамической. Лист продукта может быть быстрым и многоразовым. Инвентарь может оставаться свежим. Пользователь видит что-то сразу, не дожидаясь самой медленной части.9495## `use cache` — исполняемая документация.9697Что мне нравится в `"use cache"`, так это то, что он заставляет вас явно выражать намерение. Когда читаешь функцию, сразу понимаешь, что кто-то решил: «эти данные можно использовать повторно».9899Это особенно полезно, когда вы не используете `fetch`. Многие приложения считывают данные из Prisma, Drizzle, внутренних SDK, клиентов CMS или сервисных функций. В таких случаях старых рассуждений, основанных только на опциях `fetch`, было недостаточно.100101Эмпирическое правило:102103- содержимое кэша относительно стабильное;104- используйте детальные теги;105- оставляет динамические разрешения, сеансы, корзины, уведомления и состояния транзакций;106- поместите медленные части внутрь `Suspense`;107- измеряйте, прежде чем сказать: «Мы улучшили производительность».108109## Недействителен, не выбрасывая все110111Кэш полезен только в том случае, если вы можете его точно обновить. Здесь становятся важными `cacheTag`, `revalidateTag` и `updateTag`.112113Пример:114115```typescript116'use server';117118import { updateTag } from 'next/cache';119120export async function updateProductName(productId: string, name: string) {121 await db.product.update({122 where: { id: productId },123 data: { name },124 });125126 updateTag(`product:${productId}`);127}128```129130Важная деталь – бирка. `product:${productId}` указывает точную границу. `products` сообщает об огромном ведре. Поначалу огромное ведро удобно; через несколько месяцев это становится причиной того, что вы аннулируете половину приложения, чтобы изменить название.131132## Стабильный турбопак: то, что вы слышите каждый день133134Next.js 16 превращает Turbopack в центр разработки и сборки. Это не самая поэтичная особенность, но именно ее вы ощущаете во время работы: сервер запускается раньше, более быстрое обновление, сборки, которые перестают напоминать вынужденный перерыв на кофе.135136Тем не менее, я бы не стал переносить кодовую базу, полную пользовательских плагинов, с закрытыми глазами. Я бы проверил:137138- локальная сборка;139- нестандартный импорт;140- MDX, SVG и CSS;141- Остались плагины Webpack;142- критические страницы;143- различия во времени сборки.144145Для новых проектов я бы начал со значения по умолчанию. Для зрелых я бы сделал размеренную миграцию.146147## React Compiler: убираем шум, а не мысли148149React Compiler 1.0 стабилен, а Next.js 16 поддерживает его с помощью `reactCompiler`. Обещание состоит в том, чтобы сократить объем ручного запоминания: меньше `memo`, меньше `useMemo`, меньше `useCallback`, используемых «в целях безопасности».150151```typescript152// next.config.ts153import type { NextConfig } from 'next';154155const nextConfig: NextConfig = {156 reactCompiler: true,157};158159export default nextConfig;160```161162Я бы не стал относиться к этому как к волшебной пыли. Компилятор помогает, когда код хорошо следует правилам React. Если компоненты имеют странные побочные эффекты, скрытые мутации или неправильно используемые хуки, это необходимо сначала исправить.163164Здоровый способ попробовать:1651661. обновить `eslint-plugin-react-hooks`;1672. фиксировать фактические нарушения;1683. включить его на контролируемой территории;1694. измерять время сборки и поведение;1705. удаляйте ручное запоминание только тогда, когда оно больше не нужно.171172Цель состоит не в том, чтобы стереть все `useMemo`. Цель — прекратить писать профилактическую мемоизацию, потому что мы боимся рендеринга.173174## `proxy.ts` и граница сети175176Старый `middleware.ts` становится `proxy.ts`. Это изменение имени, но оно имеет смысл: этот файл находится на границе запроса, а не традиционное универсальное промежуточное программное обеспечение в стиле серверной части.177178```typescript179// proxy.ts180import { NextRequest, NextResponse } from 'next/server';181182export default function proxy(request: NextRequest) {183 const isLoggedIn = Boolean(request.cookies.get('session'));184185 if (!isLoggedIn && request.nextUrl.pathname.startsWith('/dashboard')) {186 return NextResponse.redirect(new URL('/login', request.url));187 }188189 return NextResponse.next();190}191```192193Правило здесь простое: пусть оно будет небольшим. Перенаправление, маршрутизация аутентификации, заголовки, существенные перезаписи. Если он начинает ощущаться как второй бэкэнд, возможно, он делает слишком много.194195## Как бы я на самом деле мигрировал196197Я бы не стал включать все функции сразу. Я бы сделал это:1981991. Я обновляю Next, React и React DOM;2002. Запускаю официальный кодмод;2013. Я исправляю критические изменения в `params`, `searchParams`, `cookies()`, `headers()` и `draftMode()`;2024. Я переношу `middleware.ts` на `proxy.ts`;2035. Проверяю сборки и критические страницы;2046. Я включаю компоненты кэша в разделе, где кеш в настоящее время создает проблемы;2057. Я определяю соглашения для тегов и аннулирования;2068. Я пробую React Compiler отдельно;2079. сравнение показателей до и после.208209Хорошая миграция — это не та миграция, которая использует все новые функции. Это делает поведение приложения более читабельным.210211## Что меняется в образе мышления212213Самое полезное в Next.js 16 — это то, что он заставляет вас лучше называть намерения. Функция — это не просто «получить товар из базы данных». Это «получите товар, я могу его кэшировать часами, я аннулирую его с помощью этого тега». Компонент — это не просто «рендеринг страницы». Это «это быстрая оболочка, это личное, это идет в потоковом режиме».214215На первый взгляд кажется, что работы больше. Тогда это становится формой спокойствия. Решения по производительности больше не скрыты в сочетании настроек по умолчанию, эвристики и племенной памяти. Они в коде.216217## Полезные источники218219- [Примечания к выпуску 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
:Next.js 16, компоненты кэша и компилятор React: что действительно меняетсяlines 1-223 (END) — press q to close