Next.js 16, רכיבי מטמון ו-React Compiler: מה באמת משתנה
· 6 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 החדשים לביטול תוקף חשובים, אבל הם סובבים סביב אותה בעיה: בניית אפליקציות מהירות מבלי לנחש מה המסגרת החליטה מאחורי הקלעים.
כי הדבר הזה חשוב
באפליקציה אמיתית אין לך רק "עמודים סטטיים" ו"דפים דינמיים". יש לך חלקים שונים עם צרכים שונים.
גיליון המוצר עשוי להשתנות מספר פעמים ביום. המחיר עשוי להשתנות לעתים קרובות יותר. הזמינות חייבת להיות כמעט חיה. שם המשתמש הוא אישי. ניתן להזרים ביקורות. סרגל הצד יכול להיות יציב. העגלה לא.
אם אתה מתייחס לכל דבר כיחידה אחת, אתה תמיד מגיע לאחד משני קצוות:
- מטמון אגרסיבי וסיכון לראות נתונים ישנים;
- עיבוד דינמי בכל מקום וביצועים גרועים מהנדרש.
רכיבי מטמון משמשים בדיוק כדי להימנע מבחירה כוזבת זו.
המודל בפועל
עם 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 אומר דלי ענק. בהתחלה הדלי הענק נוח; לאחר כמה חודשים זה הופך להיות הסיבה שאתה מבטל חצי אפליקציה כדי לשנות כותרת.
Turbopack יציב: החלק שאתה שומע כל יום
Next.js 16 מביא את Turbopack למרכז לפיתוח ובנייה. זו לא התכונה הכי פואטית, אבל היא זו שאתה מרגיש בזמן שאתה עובד: שרת שמתחיל מוקדם יותר, רענון מהיר יותר, בונים שמפסיקים להרגיש כמו הפסקת קפה מאולצת.
עם זאת, לא הייתי מעביר בסיס קוד מלא בתוספים מותאמים אישית בעיניים עצומות. הייתי בודק:
- בנייה מקומית;
- יבוא לא סטנדרטי;
- MDX, SVG ו-CSS;
- נותרו תוספים של Webpack;
- דפים קריטיים;
- הבדלים בזמני בנייה.
עבור פרויקטים חדשים, הייתי מתחיל מברירת המחדל. עבור בוגרים, הייתי עושה הגירה מדודה.
מהדר תגובה: הסר רעש, לא מחשבה
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 הוא שהוא מאלץ אותך לנקוב בכוונות טוב יותר. פונקציה היא לא רק "להשיג את המוצר ממסד הנתונים". זה "קבל את המוצר, אני יכול לשמור אותו במטמון במשך שעות, אני מבטל אותו עם התג הזה". רכיב הוא לא רק "לעבד את הדף". זה "זו הקליפה המהירה, היצירה הזו היא אישית, זו מגיעה בסטרימינג."
בהתחלה זה נראה כמו יותר עבודה. ואז זה הופך לסוג של רוגע. החלטות ביצוע אינן מוסתרות עוד בשילוב של ברירות מחדל, היוריסטיות וזיכרון שבטי. הם בקוד.