Next.js 16, Cache Components και React Compiler: τι πραγματικά αλλάζει
· 7 min read · Filippo Spinella · Next.js, React, Frontend, Performance
Για χρόνια μια από τις πιο ενοχλητικές ερωτήσεις στο Next.js ήταν: "Είναι αυτή η σελίδα στατική ή δυναμική;". Φαίνεται σαν μια απλή ερώτηση, μέχρι να προσθέσετε μια κλήση στο cookies(), ένα fetch με διαφορετικές επιλογές, ένα πρόγραμμα-πελάτη βάσης δεδομένων, ένα CMS, ένα καλάθι αγορών ή ένα κομμάτι προσαρμοσμένου περιεχομένου.
Το Next.js 16 είναι ενδιαφέρον γιατί προσπαθεί να κάνει αυτή τη συζήτηση λιγότερο μυστηριώδη. Δεν εξαλείφει την πολυπλοκότητα, αλλά αλλάζει το νοητικό μοντέλο: οι διαδρομές είναι δυναμικές από προεπιλογή, η κρυφή μνήμη δηλώνει όπου χρειάζεται και το Suspense γίνεται ο φυσικός τρόπος για να συνθέσετε γρήγορα κελύφη με μέρη που παραμένουν φρέσκα.
Το χαρακτηριστικό που πρέπει να κατανοήσετε είναι τα στοιχεία της προσωρινής μνήμης. Το Stable Turbopack, το React Compiler, το proxy.ts και τα νέα API ακύρωσης είναι σημαντικά, αλλά περιστρέφονται γύρω από το ίδιο πρόβλημα: δημιουργία γρήγορων εφαρμογών χωρίς να χρειάζεται να μαντέψετε τι αποφάσισε το πλαίσιο στα παρασκήνια.
Γιατί αυτό το πράγμα έχει σημασία
Σε μια πραγματική εφαρμογή δεν έχετε απλώς "στατικές σελίδες" και "δυναμικές σελίδες". Έχετε διαφορετικά κομμάτια με διαφορετικές ανάγκες.
Το φύλλο προϊόντος μπορεί να αλλάζει μερικές φορές την ημέρα. Η τιμή μπορεί να αλλάζει πιο συχνά. Η διαθεσιμότητα πρέπει να είναι σχεδόν ζωντανή. Το όνομα χρήστη είναι προσωπικό. Οι κριτικές μπορούν να μεταδοθούν σε ροή. Η πλαϊνή μπάρα μπορεί να είναι σταθερή. Το καλάθι όχι.
Εάν αντιμετωπίζετε τα πάντα ως μια μονάδα, καταλήγετε πάντα σε ένα από τα δύο άκρα:
- επιθετική προσωρινή αποθήκευση και κίνδυνος εμφάνισης παλαιών δεδομένων.
- δυναμική απόδοση παντού και απόδοση χειρότερη από όσο χρειάζεται.
Το Cache Components χρησιμεύει ακριβώς για την αποφυγή αυτής της λανθασμένης επιλογής.
Το μοντέλο στην πράξη
Με 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 δεν ήταν αρκετή.
Ένας εμπειρικός κανόνας:
- περιεχόμενο cachea σχετικά σταθερό.
- Χρησιμοποιήστε κοκκώδεις ετικέτες.
- αφήνει δυναμικά δικαιώματα, περιόδους σύνδεσης, καλάθια, ειδοποιήσεις και καταστάσεις συναλλαγών.
- βάλτε αργά εξαρτήματα μέσα
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.
- Απομένουν προσθήκες πακέτου ιστού.
- κρίσιμες σελίδες?
- διαφορές στους χρόνους κατασκευής.
Για νέα έργα, θα ξεκινούσα από την προεπιλογή. Για τους ώριμους, θα έκανα μια μετρημένη μετανάστευση.
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. Είναι μια αλλαγή ονόματος, αλλά είναι λογικό: αυτό το αρχείο βρίσκεται στο όριο αιτήματος, δεν είναι παραδοσιακό γενικό ενδιάμεσο λογισμικό τύπου backend.
// 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(); }
Ο κανόνας εδώ είναι απλός: κρατήστε το μικρό. Ανακατεύθυνση, δρομολόγηση εξουσιοδότησης, κεφαλίδες, βασικές επανεγγραφές. Αν αρχίσει να φαίνεται σαν δεύτερο backend, μάλλον κάνει πάρα πολλά.
Πώς πραγματικά θα μεταναστεύσω
Δεν θα ενεργοποιούσα όλες τις λειτουργίες ταυτόχρονα. Θα έκανα αυτό:
- Ενημερώνω το Next, το React και το React DOM.
- Ξεκινάω το επίσημο codemod.
- Διορθώνω τις αλλαγές σπασίματος στα
params,searchParams,cookies(),headers()καιdraftMode(); - Κάνω μετεγκατάσταση
middleware.tsσεproxy.ts; - Ελέγχω κατασκευές και κρίσιμες σελίδες.
- Ενεργοποιώ τα στοιχεία προσωρινής μνήμης σε μια ενότητα όπου η κρυφή μνήμη δημιουργεί τριβή.
- Ορίζω συμβάσεις για ετικέτες και ακύρωση.
- Δοκιμάζω το React Compiler ξεχωριστά.
- σύγκριση μετρήσεων πριν και μετά.
Η καλή μετανάστευση δεν είναι αυτή που χρησιμοποιεί όλες τις νέες δυνατότητες. Αυτό είναι που κάνει τη συμπεριφορά της εφαρμογής πιο ευανάγνωστη.
Τι αλλάζει στον τρόπο σκέψης
Το πιο χρήσιμο πράγμα για το Next.js 16 είναι ότι σας αναγκάζει να ονομάσετε καλύτερα τις προθέσεις. Μια συνάρτηση δεν είναι απλώς "πάρτε το προϊόν από τη βάση δεδομένων". Είναι "πάρτε το προϊόν, μπορώ να το κάνω cache για ώρες, το ακυρώνω με αυτήν την ετικέτα". Ένα στοιχείο δεν είναι απλώς "απόδοση της σελίδας". Είναι "αυτό είναι το γρήγορο κέλυφος, αυτό το κομμάτι είναι προσωπικό, αυτό έρχεται σε ροή."
Στην αρχή φαίνεται σαν περισσότερη δουλειά. Τότε γίνεται μια μορφή ηρεμίας. Οι αποφάσεις απόδοσης δεν κρύβονται πλέον σε έναν συνδυασμό προεπιλογών, ευρετικών και φυλετικής μνήμης. Είναι στον κώδικα.