کالبدشکافی کش در Next.js 15: از fetch تا use cache و PPR
نویسنده: آرش لطیفی
کش در Next.js 15 زیر و رو شده: fetch memoization، unstable_cache، use cache و Partial Prerendering. بررسی دقیق معماری، کد و تلههای پروداکشن.
چکیده (TL;DR): تو Next.js 15 همهچی پیشفرض بدون کشه — مگه خودت بگی «اینو بذار انبار». انبار ۴ بخش داره (یادداشت موقت، انبار مرکزی، ویترین آماده، جیب مرورگر). برای دادهی مشترک
revalidate+tagsبذار، برای دادهی شخصی حتماprivateبگو، و با PPR اسکلت صفحه رو از انبار بده و فقط تیکههای شخصی رو زنده استریم کن.
اگه با Next.js 14 کار کردی، یادته fetch خودش همهچی رو میذاشت انبار و تو باید cache: 'no-store' میزدی تا جلوشو بگیری. تو Next.js 15 بازی برعکس شده: هیچی نمیره انبار مگه خودت بگی. این یه تغییر کوچیکه که کلی سردرگمی رو تو پروداکشن کم میکنه.
توی این مطلب با زبون خودمونی و یه استعارهی ساده (انبار!) میشکافیم که هر تیکهی کش چیکار میکنه و کِی به دردت میخوره.
انبار Next.js چهار بخشه — قاطی نکن!
فکر کن Next.js یه انباره با ۴ قسمت:
| بخش انبار | کجاست؟ | چیکار میکنه؟ |
|---|---|---|
| دفتر یادداشت موقت (Request Memoization) | فقط برای یه درخواست | اگه تو یه رندر دوبار همون fetch رو زدی، بار دوم از دفترچه میخونه — نه از اینترنت |
| انبار مرکزی (Data Cache) | روی سرور، برای همه | دادهای که گفتی revalidate یا use cache داره، اینجا میمونه |
| ویترین آماده (Full Route Cache) | روی سرور | HTML آمادهی صفحههای استاتیک — همون که تو بیلد چیده شده |
| جیب مرورگر (Router Cache) | تو مرورگر کاربر | RSC که قبلا گرفته رو تو جیبش نگه میداره تا برگشت به صفحه قبل سریع باشه |
سردرگمی معروف: «کش رو خاموش کردم ولی هنوز دادهی قدیمی میده!» — معمولا انبار مرکزی رو خالی کردی ولی ویترین هنوز همون HTML قدیمی رو میده.
قبل و بعد — چی عوض شده؟
قبل (Next.js 14): همهچی خودکار میرفت انبار. برای زنده موندن باید داد میزدی no-store.
بعد (Next.js 15): هیچی نمیره انبار مگه خودت بگی. راحتتر و امنتره.
خلاصه: دیگه ناخواسته دادهی کهنه به کاربر نمیدی.
use cache — هر تابعی رو بذار انبار، نه فقط fetch
قبلا فقط fetch رو میشد انباری کرد. حالا با use cache هر تابع async — حتی کوئری دیتابیس — رو میذاری انبار:
با برچسب و زمان تازهسازی:
نکتهی خیلی مهم: اگه داده مالِ یه کاربر خاصه (مثلا پیشنهادات شخصی)، حتما private بذار. اگه یادت بره، پیشنهادِ کاربر A میره برای کاربر B — افتضاحه!
اگه use cache نداری — unstable_cache همون کار رو میکنه
روی نسخهی stable که هنوز use cache نیومده:
فرقش؟ unstable_cache فقط روی سروره ولی use cache قراره با کامپوننت و PPR هم قاطی بشه.
PPR — مثلِ ویترین + حفرهی زنده
PPR رو اینجوری ببین: ویترین مغازه رو از قبل میچینی (استاتیک)، فقط جایِ قیمتِ لحظهای رو خالی میذاری تا موقعِ بازدید پر بشه (داینامیک).
قبل از PPR باید کل صفحه رو یا استاتیک یا داینامیک میکردی. با PPR میگی: اسکلت صفحه از انبار بیاد، فقط تیکههای شخصی زنده لود بشن.
روشن کردنش تو next.config.ts:
نتیجه؟ سرعتِ استاتیک + شخصیسازیِ واقعی. بدون PPR مجبور بودی کل صفحه رو داینامیک کنی و سرعت رو بدی بره.
تلههای پروداکشن — اینا رو حفظ باش
۱. دست زدن به cookies() کل ویترین رو خراب میکنه.
کافیه تو همون فایلِ صفحه cookies() یا headers() رو صدا بزنی، کل صفحه داینامیک میشه — حتی اگه فکر میکردی استاتیکه. راهحل: ببرش تو Suspense.
۲. بدون برچسب، revalidateTag هیچ غلطی نمیکنه.
اگه موقع fetch یا use cache تگ نذاشته باشی، بعدا هرچی revalidateTag بزنی انبار تکون نمیخوره.
۳. جیب مرورگر گولت میزنه.
بعد از اینکه انبار مرکزی رو خالی کردی (revalidateTag)، کاربر هنوز دادهی قدیمی میبینه چون تو جیبِ مرورگرش (Router Cache) مونده. برای تست router.refresh() بزن یا هارد رفرش کن.
۴. انبار خصوصی یادت نره.
هر وقت داده به کاربر وصله، private بذار و حتما تست کن کاربر A دادهی B رو نبینه.
چکلیست سریع (بزن به دیوار!)
- [ ] رو Next.js 15 فرض کن هیچی انباری نیست مگه خودت بگی.
- [ ] دادهی مشترک؟
revalidate+tags+ موقع تغییرrevalidateTagبزن. - [ ] دادهی شخصی؟ حتما
use cache: privateیاunstable_cacheبا کلیدuserId. - [ ] صفحه رو با PPR بساز: اسکلتِ آماده + حفرههای
Suspense. - [ ]
cookies()وheaders()رو هیچوقت تو بدنهی اصلی صفحه صدا نزن — فقط توSuspense.
اگه میخوای یه نمونهی واقعیِ سریع و سئو شده با همین الگو ببینی، سری به پروژههای ما بزن — بیشترشون دقیقا با همین انبار و PPR ساخته شدن.
سوالی دربارهی مهاجرت از مدل قدیمی داری؟ من آرش لطیفی هستم؛ از طریق تماس بنویس تا سناریوی پروژهت رو با هم چک کنیم.
برای مطالب خودمونیِ فنی بیشتر، آرشیو بخش تک رو ببین.