spinny:~/writing $ vim nextjs-16-cache-components.md
1~2I årevis har et af de mest irriterende spørgsmål i Next.js været: "Er denne side statisk eller dynamisk?". Det virker som et simpelt spørgsmål, indtil du tilføjer et opkald til `cookies()`, en `fetch` med forskellige muligheder, en databaseklient, et CMS, en indkøbskurv eller et stykke tilpasset indhold.3~4Next.js 16 er interessant, fordi den forsøger at gøre denne samtale mindre mystisk. Det eliminerer ikke kompleksitet, men det flytter den mentale model: ruter er dynamiske som standard, cachen erklærer sig selv, hvor det er nødvendigt, og `Suspense` bliver den naturlige måde at komponere hurtige skaller med dele, der forbliver friske.5~6Funktionen, der skal forstås, er Cache-komponenter. Stabil Turbopack, React Compiler, `proxy.ts` og de nye invaliderings-API'er er vigtige, men de kredser om det samme problem: at bygge hurtige apps uden at skulle gætte, hvad rammerne besluttede bag kulisserne.7~8## Fordi denne ting betyder noget9~10I en rigtig app har du ikke bare "statiske sider" og "dynamiske sider". Du har forskellige stykker med forskellige behov.11~12Produktarket kan ændres et par gange om dagen. Prisen kan ændre sig oftere. Tilgængeligheden skal være næsten live. Brugernavnet er personligt. Anmeldelser kan streames. Sidebjælken kan være stabil. Det gør vognen ikke.13~14Hvis du behandler alt som én enhed, ender du altid i en af to yderpunkter:15~16- aggressiv caching og risiko for at se gamle data;17- dynamisk gengivelse overalt og ydeevne dårligere end nødvendigt.18~19Cache-komponenter tjener netop til at undgå dette falske valg.20~21## Modellen i praksis22~23Med `cacheComponents: true` kan du erklære, hvad der kan cachelagres ved hjælp af `"use cache"`. Derefter kan du knytte varighed og tags til `cacheLife()` og `cacheTag()`. Dynamiske dele forbliver dynamiske og kan isoleres med `Suspense`.24~25```mermaid26flowchart TD27 Request[Brugeranmodning] --> Shell[Cachelagret skal]28 Request --> Dynamic[Dynamiske sektioner]29 Shell --> FirstPaint[Første hurtige indhold]30 Dynamic --> Stream[Streaming inde i Suspense]31 Stream --> Complete[Hele siden]32```33~34Opsætningen er lille:35~36```typescript37// next.config.ts38import type { NextConfig } from 'next';39~40const nextConfig: NextConfig = {41 cacheComponents: true,42};43~44export default nextConfig;45```46~47Den store ændring er ikke i konfigurationen. Det er i, hvordan du begynder at skrive komponenterne.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~93Siden behøver ikke at være helt i cache eller være dynamisk. Produktarket kan være hurtigt og genanvendeligt. Beholdningen kan forblive frisk. Brugeren ser noget med det samme, uden at vente på den langsomste del.94~95## `use cache` er eksekverbar dokumentation96~97Det, jeg godt kan lide ved `"use cache"`, er, at det tvinger dig til at udtrykke en intention. Når du læser en funktion, forstår du med det samme, at nogen har besluttet: "disse data kan genbruges".98~99Det er især nyttigt, når du ikke bruger `fetch`. Mange apps læser data fra Prisma, Drizzle, interne SDK'er, CMS-klienter eller servicefunktioner. I disse tilfælde var den gamle begrundelse kun baseret på `fetch` muligheder ikke nok.100~101En tommelfingerregel:102~103- cacheindhold relativt stabilt;104- brug granulære tags;105- efterlader dynamiske tilladelser, sessioner, vogne, meddelelser og transaktionstilstande;106- sæt langsomme dele i `Suspense`;107- måle før du siger "vi forbedrede ydeevnen".108~109## Ugyldig uden at smide alt væk110~111Cachen er kun nyttig, hvis du kan opdatere den nøjagtigt. Her bliver `cacheTag`, `revalidateTag` og `updateTag` vigtige.112~113Eksempel: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~130Den vigtige detalje er mærket. `product:${productId}` fortæller en præcis grænse. `products` fortæller en kæmpe spand. Først er den enorme spand behagelig; efter et par måneder bliver det grunden til, at du ugyldiggør en halv app for at ændre en titel.131~132## Stabil Turbopack: den del, du hører hver dag133~134Next.js 16 bringer Turbopack til centrum for udvikling og opbygning. Det er ikke det mest poetiske træk, men det er det, du føler, mens du arbejder: server, der starter tidligere, hurtigere opdatering, opbygninger, der holder op med at føles som en tvungen kaffepause.135~136Når det er sagt, ville jeg ikke migrere en kodebase fuld af brugerdefinerede plugins med lukkede øjne. Jeg ville tjekke:137~138- lokal opbygning;139- ikke-standard import;140- MDX, SVG og CSS;141- Webpack plugins tilbage;142- kritiske sider;143- forskelle i byggetider.144~145For nye projekter ville jeg starte fra standard. For modne mennesker ville jeg lave en afmålt migration.146~147## React Compiler: fjern støj, ikke tænkt148~149React Compiler 1.0 er stabil og Next.js 16 understøtter den med `reactCompiler`. Løftet er at reducere en masse manuel memoisering: mindre `memo`, mindre `useMemo`, mindre `useCallback` brugt "for sikkerheden".150~151```typescript152// next.config.ts153import type { NextConfig } from 'next';154~155const nextConfig: NextConfig = {156 reactCompiler: true,157};158~159export default nextConfig;160```161~162Jeg ville ikke behandle det som et magisk støv. Compileren hjælper, når koden følger React-reglerne godt. Hvis komponenter har mærkelige bivirkninger, skjulte mutationer eller dårligt brugte kroge, skal det rettes først.163~164Den sunde måde at prøve det på:165~1661. opdatering `eslint-plugin-react-hooks`;1672. rette faktiske overtrædelser;1683. aktivere det på et kontrolleret område;1694. måle byggetid og adfærd;1705. fjern kun manuel huskeseddel, når det ikke længere er nødvendigt.171~172Målet er ikke at slette hver `useMemo`. Målet er at stoppe med at skrive forebyggende huskeseddel, fordi vi er bange for at gengive.173~174## `proxy.ts` og netværksgrænsen175~176Den gamle `middleware.ts` bliver `proxy.ts`. Det er en navneændring, men det giver mening: den fil sidder ved anmodningsgrænsen, det er ikke traditionel backend-stil generisk middleware.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~193Reglen her er enkel: hold den lille. Omdirigering, godkendelsesrouting, overskrifter, væsentlige omskrivninger. Hvis det begynder at føles som en anden backend, gør det sandsynligvis for meget.194~195## Hvordan jeg virkelig ville migrere196~197Jeg ville ikke slå alle funktionerne til på én gang. Jeg ville gøre dette:198~1991. Jeg opdaterer Next, React og React DOM;2002. Jeg starter den officielle kodemod;2013. Jeg retter brydende ændringer på `params`, `searchParams`, `cookies()`, `headers()` og `draftMode()`;2024. Jeg migrerer `middleware.ts` til `proxy.ts`;2035. Jeg tjekker builds og kritiske sider;2046. Jeg aktiverer Cache-komponenter på en sektion, hvor cachen i øjeblikket skaber friktion;2057. Jeg definerer konventioner for tags og invalidering;2068. Jeg prøver React Compiler separat;2079. sammenligning af målinger før og efter.208~209Den gode migration er ikke den, der bruger alle de nye funktioner. Det er det, der gør appens adfærd mere læsbar.210~211## Hvad ændrer måden at tænke på212~213Det mest nyttige ved Next.js 16 er, at det tvinger dig til at nævne intentioner bedre. En funktion er ikke bare "hent produktet fra databasen". Det er "få produktet, jeg kan cache det i timevis, jeg gør det ugyldigt med dette tag". En komponent er ikke bare "render siden". Det er "dette er den hurtige skal, dette stykke er personligt, det kommer streaming."214~215Umiddelbart virker det som mere arbejde. Så bliver det en form for ro. Ydeevnebeslutninger er ikke længere skjult i en kombination af standardindstillinger, heuristik og stammehukommelse. De er i koden.216~217## Nyttige kilder218~219- [Next.js 16 release notes](https://nextjs.org/blog/next-16)220- [Cache-komponenter - Next.js docs](https://nextjs.org/docs/app/getting-started/cache-components)221- [brug cache - Next.js docs](https://nextjs.org/docs/app/api-reference/directives/use-cache)222- [React Compiler 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