spinny:~/writing $ vim nextjs-16-cache-components.md
1~2במשך שנים אחת השאלות הכי מעצבנות ב-Next.js הייתה: "האם הדף הזה סטטי או דינמי?". זה נראה כמו שאלה פשוטה, עד שאתה מוסיף קריאה ל-`cookies()`, `fetch` עם אפשרויות שונות, לקוח מסד נתונים, CMS, עגלת קניות או פיסת תוכן מותאם אישית.3~4Next.js 16 מעניין כי הוא מנסה להפוך את השיחה הזו לפחות מסתורית. זה לא מבטל את המורכבות, אבל זה משנה את המודל המנטלי: מסלולים הם דינמיים כברירת מחדל, המטמון מכריז על עצמו היכן שצריך, ו-`Suspense` הופך לדרך הטבעית להרכיב קליפות מהירות עם חלקים שנשארים טריים.5~6התכונה שצריך להבין היא רכיבי מטמון. Stable 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## Turbopack יציב: החלק שאתה שומע כל יום133~134Next.js 16 מביא את Turbopack למרכז לפיתוח ובנייה. זו לא התכונה הכי פואטית, אבל היא זו שאתה מרגיש בזמן שאתה עובד: שרת שמתחיל מוקדם יותר, רענון מהיר יותר, בונים שמפסיקים להרגיש כמו הפסקת קפה מאולצת.135~136עם זאת, לא הייתי מעביר בסיס קוד מלא בתוספים מותאמים אישית בעיניים עצומות. הייתי בודק:137~138- בנייה מקומית;139- יבוא לא סטנדרטי;140- MDX, SVG ו-CSS;141- נותרו תוספים של Webpack;142- דפים קריטיים;143- הבדלים בזמני בנייה.144~145עבור פרויקטים חדשים, הייתי מתחיל מברירת המחדל. עבור בוגרים, הייתי עושה הגירה מדודה.146~147## מהדר תגובה: הסר רעש, לא מחשבה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`. זה שינוי שם, אבל זה הגיוני: הקובץ הזה יושב על גבול הבקשה, זה לא תוכנת תווך גנרית מסורתית בסגנון Backend.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הכלל כאן הוא פשוט: שמור על זה קטן. הפניה מחדש, ניתוב אימות, כותרות, שכתובים חיוניים. אם זה מתחיל להרגיש כמו backend שני, זה כנראה עושה יותר מדי.194~195## איך באמת הייתי נודד196~197לא הייתי מפעיל את כל התכונות בבת אחת. הייתי עושה את זה:198~1991. אני מעדכן את Next, React ו-React DOM;2002. אני משיק את ה-codemod הרשמי;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 docs](https://nextjs.org/docs/app/getting-started/cache-components)221- [השתמש במטמון - 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