spinny:~/writing $ less nextjs-16-cache-components.md
12Trong nhiều năm, một trong những câu hỏi khó chịu nhất trong Next.js là: "Trang này tĩnh hay động?". Đây có vẻ là một câu hỏi đơn giản cho đến khi bạn thêm lệnh gọi tới `cookies()`, `fetch` với các tùy chọn khác nhau, ứng dụng khách cơ sở dữ liệu, CMS, giỏ hàng hoặc một phần nội dung tùy chỉnh.34Next.js 16 thú vị vì nó cố gắng làm cho cuộc trò chuyện này bớt bí ẩn hơn. Nó không loại bỏ sự phức tạp, nhưng nó thay đổi mô hình tinh thần: các tuyến đường mặc định là động, bộ đệm tự khai báo khi cần thiết và `Suspense` trở thành cách tự nhiên để tạo ra các shell nhanh với các bộ phận luôn mới.56Tính năng cần hiểu là Cache Components. Turbopack ổn định, Trình biên dịch React, `proxy.ts` và các API vô hiệu hóa mới rất quan trọng nhưng chúng xoay quanh cùng một vấn đề: xây dựng các ứng dụng nhanh mà không cần phải đoán xem framework đã quyết định điều gì đằng sau hậu trường.78## Bởi vì điều này quan trọng910Trong ứng dụng thực, bạn không chỉ có "trang tĩnh" và "trang động". Bạn có những phần khác nhau với những nhu cầu khác nhau.1112Bảng sản phẩm có thể thay đổi vài lần trong ngày. Giá có thể thay đổi thường xuyên hơn. Tính khả dụng phải gần như trực tiếp. Tên người dùng là cá nhân. Đánh giá có thể được truyền trực tuyến. Thanh bên có thể ổn định. Xe đẩy thì không.1314Nếu bạn coi mọi thứ như một đơn vị, bạn sẽ luôn rơi vào một trong hai thái cực:1516- bộ nhớ đệm tích cực và nguy cơ nhìn thấy dữ liệu cũ;17- hiển thị động ở mọi nơi và hiệu suất kém hơn mức cần thiết.1819Thành phần bộ đệm phục vụ chính xác để tránh lựa chọn sai lầm này.2021## Mô hình trong thực tế2223Với `cacheComponents: true`, bạn có thể khai báo những gì có thể lưu vào bộ nhớ đệm bằng `"use cache"`. Sau đó, bạn có thể liên kết thời lượng và thẻ với `cacheLife()` và `cacheTag()`. Các bộ phận động vẫn duy trì trạng thái động và có thể được tách biệt bằng `Suspense`.2425```mermaid26flowchart TD27 Request[Yêu cầu của người dùng] --> Shell[Vỏ được lưu trong bộ nhớ đệm]28 Request --> Dynamic[Phần động]29 Shell --> FirstPaint[Nội dung nhanh đầu tiên]30 Dynamic --> Stream[Truyền phát bên trong Hồi hộp]31 Stream --> Complete[Toàn trang]32```3334Thiết lập nhỏ:3536```typescript37// next.config.ts38import type { NextConfig } from 'next';3940const nextConfig: NextConfig = {41 cacheComponents: true,42};4344export default nextConfig;45```4647Sự thay đổi lớn không nằm ở cấu hình. Đó là cách bạn bắt đầu viết các thành phần.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```9293Trang không nhất thiết phải được lưu hoàn toàn vào bộ nhớ đệm hoặc hoàn toàn động. Bảng sản phẩm có thể nhanh chóng và có thể tái sử dụng. Hàng tồn kho có thể vẫn còn mới. Người dùng sẽ nhìn thấy thứ gì đó ngay lập tức mà không cần chờ đợi phần chậm nhất.9495## `use cache` là tài liệu thực thi9697Điều tôi thích ở `"use cache"` là nó buộc bạn phải đưa ra ý định rõ ràng. Khi đọc một hàm, bạn hiểu ngay rằng ai đó đã quyết định: "dữ liệu này có thể được sử dụng lại".9899Nó đặc biệt hữu ích khi bạn không sử dụng `fetch`. Nhiều ứng dụng đọc dữ liệu từ Prisma, Drizzle, SDK nội bộ, ứng dụng khách CMS hoặc chức năng dịch vụ. Trong những trường hợp đó, lý luận cũ chỉ dựa trên các tùy chọn `fetch` là không đủ.100101Một nguyên tắc nhỏ:102103- nội dung cachea tương đối ổn định;104- sử dụng thẻ chi tiết;105- để lại các quyền, phiên, giỏ hàng, thông báo và trạng thái giao dịch động;106- đặt các phần chậm bên trong `Suspense`;107- đo lường trước khi nói "chúng tôi đã cải thiện hiệu suất".108109## Vô hiệu hóa mà không vứt bỏ mọi thứ110111Bộ đệm chỉ hữu ích nếu bạn có thể cập nhật nó một cách chính xác. Ở đây `cacheTag`, `revalidateTag` và `updateTag` trở nên quan trọng.112113Ví dụ: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```129130Chi tiết quan trọng là thẻ. `product:${productId}` cho biết ranh giới chính xác. `products` kể về một cái thùng lớn. Lúc đầu, chiếc xô lớn rất thoải mái; sau một vài tháng, nó trở thành lý do khiến bạn vô hiệu hóa một nửa ứng dụng để thay đổi tiêu đề.131132## Turbopack ổn định: phần bạn nghe thấy hàng ngày133134Next.js 16 đưa Turbopack vào trung tâm phát triển và xây dựng. Đây không phải là tính năng thơ mộng nhất, nhưng là tính năng bạn cảm nhận được khi làm việc: máy chủ khởi động sớm hơn, làm mới nhanh hơn, xây dựng khiến bạn không còn cảm giác như đang phải nghỉ giải lao ép buộc để uống cà phê.135136Điều đó có nghĩa là tôi sẽ không di chuyển một cơ sở mã chứa đầy các plugin tùy chỉnh khi nhắm mắt lại. Tôi sẽ kiểm tra:137138- xây dựng địa phương;139- nhập khẩu không đạt tiêu chuẩn;140- MDX, SVG và CSS;141- Các plugin webpack còn lại;142- các trang quan trọng;143- sự khác biệt về thời gian xây dựng.144145Đối với các dự án mới, tôi sẽ bắt đầu từ mặc định. Đối với những người trưởng thành, tôi sẽ thực hiện di chuyển có đo lường.146147## Trình biên dịch React: loại bỏ tiếng ồn, không suy nghĩ148149React Compiler 1.0 ổn định và Next.js 16 hỗ trợ nó với `reactCompiler`. Lời hứa là sẽ giảm bớt nhiều thao tác ghi nhớ thủ công: ít `memo` hơn, ít `useMemo` hơn, ít `useCallback` được sử dụng "vì an toàn".150151```typescript152// next.config.ts153import type { NextConfig } from 'next';154155const nextConfig: NextConfig = {156 reactCompiler: true,157};158159export default nextConfig;160```161162Tôi sẽ không coi nó như bụi ma thuật. Trình biên dịch sẽ giúp ích khi mã tuân thủ tốt các quy tắc React. Nếu các thành phần có tác dụng phụ lạ, đột biến ẩn hoặc hook bị sử dụng không tốt thì cần phải khắc phục trước tiên.163164Cách lành mạnh để thử nó:1651661. cập nhật `eslint-plugin-react-hooks`;1672. khắc phục các vi phạm thực tế;1683. kích hoạt nó trên một khu vực được kiểm soát;1694. đo lường thời gian và hành vi xây dựng;1705. chỉ xóa ghi nhớ thủ công khi không còn cần thiết.171172Mục tiêu không phải là xóa mọi `useMemo`. Mục đích là ngừng ghi nhớ phòng ngừa vì chúng tôi sợ hiển thị.173174## `proxy.ts` và ranh giới mạng175176`middleware.ts` cũ trở thành `proxy.ts`. Đó là một sự thay đổi tên, nhưng nó có ý nghĩa: tệp đó nằm ở ranh giới yêu cầu, nó không phải là phần mềm trung gian chung kiểu phụ trợ truyền thống.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```192193Quy tắc ở đây rất đơn giản: giữ nó ở mức nhỏ. Chuyển hướng, định tuyến xác thực, tiêu đề, viết lại cần thiết. Nếu nó bắt đầu có cảm giác giống như một chương trình phụ trợ thứ hai thì có lẽ nó đang làm quá nhiều việc.194195## Làm thế nào tôi thực sự có thể di cư196197Tôi sẽ không bật tất cả các tính năng cùng một lúc. Tôi sẽ làm điều này:1981991. Tôi cập nhật Next, React và React DOM;2002. Tôi khởi chạy codemod chính thức;2013. Tôi sửa các thay đổi có thể gây lỗi trên `params`, `searchParams`, `cookies()`, `headers()` và `draftMode()`;2024. Tôi di chuyển `middleware.ts` sang `proxy.ts`;2035. Tôi kiểm tra các bản dựng và các trang quan trọng;2046. Tôi kích hoạt Thành phần bộ đệm trên một phần mà bộ đệm hiện đang tạo ra xung đột;2057. Tôi xác định các quy ước cho thẻ và tính vô hiệu;2068. Tôi thử riêng Trình biên dịch React;2079. so sánh các chỉ số trước và sau.208209Việc di chuyển tốt không phải là việc sử dụng tất cả các tính năng mới. Đó là điều làm cho hoạt động của ứng dụng trở nên dễ đọc hơn.210211## Điều gì thay đổi trong cách suy nghĩ212213Điều hữu ích nhất ở Next.js 16 là nó buộc bạn phải đặt tên cho ý định tốt hơn. Chức năng không chỉ là "lấy sản phẩm từ cơ sở dữ liệu". Đó là "lấy sản phẩm, tôi có thể lưu nó vào bộ nhớ đệm hàng giờ, tôi vô hiệu hóa nó bằng thẻ này". Một thành phần không chỉ là "kết xuất trang". Đó là "đây là phần vỏ nhanh, phần này mang tính cá nhân, phần này phát trực tuyến."214215Lúc đầu có vẻ như có nhiều việc hơn. Sau đó, nó trở thành một hình thức bình tĩnh. Các quyết định về hiệu suất không còn bị ẩn giấu trong sự kết hợp của các giá trị mặc định, phương pháp phỏng đoán và trí nhớ bộ lạc. Chúng nằm trong mật mã.216217## Nguồn hữu ích218219- [Ghi chú phát hành Next.js 16](https://nextjs.org/blog/next-16)220- [Thành phần bộ nhớ đệm - Tài liệu Next.js](https://nextjs.org/docs/app/getting-started/cache-components)221- [sử dụng bộ nhớ đệm - Tài liệu Next.js](https://nextjs.org/docs/app/api-reference/directives/use-cache)222- [Trình biên dịch React v1.0](https://react.dev/blog/2025/10/07/react-compiler-1)223
:Next.js 16, Thành phần bộ đệm và Trình biên dịch React: điều gì thực sự thay đổilines 1-223 (END) — press q to close