spinny:~/writing $ less nextjs-16-cache-components.md
12במשך שנים אחת השאלות הכי מעצבנות ב-Next.js הייתה: "האם הדף הזה סטטי או דינמי?". זה נראה כמו שאלה פשוטה, עד שאתה מוסיף קריאה ל-`cookies()`, `fetch` עם אפשרויות שונות, לקוח מסד נתונים, CMS, עגלת קניות או פיסת תוכן מותאם אישית.34Next.js 16 מעניין כי הוא מנסה להפוך את השיחה הזו לפחות מסתורית. זה לא מבטל את המורכבות, אבל זה משנה את המודל המנטלי: מסלולים הם דינמיים כברירת מחדל, המטמון מכריז על עצמו היכן שצריך, ו-`Suspense` הופך לדרך הטבעית להרכיב קליפות מהירות עם חלקים שנשארים טריים.56התכונה שצריך להבין היא רכיבי מטמון. Stable 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## Turbopack יציב: החלק שאתה שומע כל יום133134Next.js 16 מביא את Turbopack למרכז לפיתוח ובנייה. זו לא התכונה הכי פואטית, אבל היא זו שאתה מרגיש בזמן שאתה עובד: שרת שמתחיל מוקדם יותר, רענון מהיר יותר, בונים שמפסיקים להרגיש כמו הפסקת קפה מאולצת.135136עם זאת, לא הייתי מעביר בסיס קוד מלא בתוספים מותאמים אישית בעיניים עצומות. הייתי בודק:137138- בנייה מקומית;139- יבוא לא סטנדרטי;140- MDX, SVG ו-CSS;141- נותרו תוספים של Webpack;142- דפים קריטיים;143- הבדלים בזמני בנייה.144145עבור פרויקטים חדשים, הייתי מתחיל מברירת המחדל. עבור בוגרים, הייתי עושה הגירה מדודה.146147## מהדר תגובה: הסר רעש, לא מחשבה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`. זה שינוי שם, אבל זה הגיוני: הקובץ הזה יושב על גבול הבקשה, זה לא תוכנת תווך גנרית מסורתית בסגנון Backend.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הכלל כאן הוא פשוט: שמור על זה קטן. הפניה מחדש, ניתוב אימות, כותרות, שכתובים חיוניים. אם זה מתחיל להרגיש כמו backend שני, זה כנראה עושה יותר מדי.194195## איך באמת הייתי נודד196197לא הייתי מפעיל את כל התכונות בבת אחת. הייתי עושה את זה:1981991. אני מעדכן את Next, React ו-React DOM;2002. אני משיק את ה-codemod הרשמי;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 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
:Next.js 16, רכיבי מטמון ו-React Compiler: מה באמת משתנהlines 1-223 (END) — press q to close