14 điểm bởi xguru 2025-02-24 | 3 bình luận | Chia sẻ qua WhatsApp
  • Định hướng tương lai của Next.js rất thú vị
  • Đã có một số vấn đề với Server Actions, nhưng có thể thấy khả năng cải thiện nhờ useOptimisticuseFormStatus của React 19
    • Cách tiếp cận useFetcher của Remix cũng mang lại DX rất tốt
  • PPR (Partial Pre-rendering) và hệ thống cache chi tiết mới của Next.js đặc biệt nổi bật
  • Nhìn chung, tôi có ấn tượng rất tích cực

Bức tranh tổng thể

  • Có thể bật thử nghiệm hệ thống cache mới trong next.config.js
  • Có thể định nghĩa cache profile để thiết lập các mốc thời gian hết hạn và chu kỳ tái xác thực khác nhau
// next.config.js  
const config = {  
  experimental: {  
    // Bật hệ thống cache mới. Giờ có thể dùng `use cache` trong code   
    dynamicIO: true,   
    // Tùy chọn: thiết lập cache profile   
    cacheLife: {  
      blog: {  
        stale: 3600, // Giữ cache phía client: 1 giờ  
        revalidate: 900, // Làm mới từ server: 15 phút  
        expire: 86400, // Tuổi thọ tối đa: 1 ngày  
      },  
    },  
  },  
};  

Cách dùng cơ bản của use cache

  • Có thể cache ở cấp file, component hoặc hàm bằng khai báo "use cache"
  • Trong ví dụ code, chỉ cần thêm use cache là có thể áp dụng cache dễ dàng
  • Có thể dùng cacheTag, revalidateTag... để vô hiệu hóa cache vào đúng thời điểm mong muốn
// 1. Cache ở cấp file  
"use cache";  
export default function Page() {  
  return <div>Cached Page</div>;  
}  
  
// 2. Cache ở cấp component  
export async function PriceDisplay() {  
  "use cache";  
  const price = await fetchPrice();  
  return <div>${price}</div>;  
}  
  
// 3. Cache ở cấp hàm  
export async function getData() {  
  "use cache";  
  return await db.query();  
}  

Cache dựa trên tag

import { unstable_cacheTag as cacheTag, revalidateTag } from 'next/cache';  
  
// Cache một nhóm dữ liệu cụ thể  
export async function ProductList() {  
  'use cache';  
  cacheTag('products');  
  const products = await fetchProducts();  
  return <div>{products}</div>;  
}  
  
// Vô hiệu hóa cache khi dữ liệu thay đổi  
export async function addProduct() {  
  'use server';  
  await db.products.add(...);  
  revalidateTag('products');  
}  

Cache profile tùy chỉnh

  • Có thể dùng unstable_cacheLife để lấy cache profile đã định nghĩa trong next.config.js
  • Áp dụng chính sách cache bằng tên profile được khai báo trong code (ví dụ: "blog")
import { unstable_cacheLife as cacheLife } from "next/cache";  
  
export async function BlogPosts() {  
  "use cache";  
  cacheLife("blog"); // Dùng cache profile blog đã định nghĩa sẵn  
  return await fetchPosts();  
}  

Những điểm quan trọng nhưng dễ bị bỏ qua

Tự động tạo cache key

  • propsarguments của component sẽ tự động được đưa vào cache key
  • Các giá trị không thể serialize (như hàm) sẽ được xử lý dưới dạng "tham chiếu không thể sửa đổi"
export async function UserCard({ id, onDelete }) {  
  "use cache";  
  // id được đưa vào cache key  
  // onDelete vẫn được truyền vào nhưng không ảnh hưởng đến cache  
  const user = await fetchUser(id);  
  return <div onClick={onDelete}>{user.name}</div>;  
}  

Trộn nội dung động và nội dung được cache

  • Có thể truyền nội dung động vào bên trong nội dung đã cache dưới dạng phần tử con để dùng kết hợp
  • Có thể chỉ định mảng cacheTag để áp dụng và vô hiệu hóa nhiều tag cùng lúc
export async function CachedWrapper({ children }) {  
  "use cache";  
  const header = await fetchHeader();  
  return (  
    <div>  
      <h1>{header}</h1>  
      {children} {/* Nội dung động vẫn được giữ nguyên */}  
    </div>  
  );  
}  
export async function ProductPage({ id }) {  
  "use cache";  
  cacheTag(["products", `product-${id}`, "featured"]);  
  // Có thể vô hiệu hóa bằng bất kỳ tag nào trong số này  
}  

Cấu trúc phân tầng của cache

  • Khi khai báo "use cache" ở cấp cao nhất, toàn bộ vùng đó sẽ được cache
  • Có thể loại trừ một số phần cụ thể (ví dụ: section động dùng Suspense) khỏi vùng cache
"use cache";  
export default async function Page() {  
  return (  
    <div>  
      <CachedHeader />  
      <div>  
        <Suspense fallback={<Loading />}>  
          <DynamicFeed /> {/* Nội dung động */}  
        </Suspense>  
      </div>  
    </div>  
  );  
}  

Tính an toàn kiểu dữ liệu

  • Có thể quản lý các chuỗi như cache key và cache profile dưới dạng hằng số để giảm việc dùng magic string
  • Sẽ thuận tiện nếu dùng cách tạo tag theo pattern giống React Query
// Quản lý key của cache profile bằng hằng số  
export const CACHE_LIFE_KEYS = {  
  blog: "blog",  
} as const;  
  
const config = {  
  experimental: {  
    cacheLife: {  
      [CACHE_LIFE_KEYS.blog]: {  
        stale: 3600,  
        revalidate: 900,  
        expire: 86400,  
      },  
    },  
  },  
};  

Cách quản lý tag cache hiệu quả

  • Áp dụng tag factory pattern theo kiểu React Query
export const CACHE_TAGS = {  
  blog: {  
    all: ["blog"] as const,  
    list: () => [...CACHE_TAGS.blog.all, "list"] as const,  
    post: (id: string) => [...CACHE_TAGS.blog.all, "post", id] as const,  
    comments: (postId: string) =>  
      [...CACHE_TAGS.blog.all, "post", postId, "comments"] as const,  
  },  
} as const;  
  
// Thiết lập tag cache  
function tagCache(tags: string[]) {  
  cacheTag(...tags);  
}  
  
// Ví dụ sử dụng  
export async function BlogList() {  
  "use cache";  
  tagCache(CACHE_TAGS.blog.list());  
}  

3 bình luận

 
schang124 2025-03-03

Có vẻ chỉ nên dùng các framework như Next.js hoặc Remix khi SEO quan trọng và vì thế cần SSR.

Đặc biệt, tôi nghĩ cần cân nhắc kỹ khi áp dụng Next.js cho các dịch vụ mà SEO không quan trọng, như sản phẩm kinh doanh B2B hay back office. Bởi giao diện mà Next.js áp đặt và độ phức tạp của nó có thể làm giảm năng suất phát triển.

Cá nhân tôi thấy rằng nếu không cần SEO thì Vite + React tốt hơn nhiều về năng suất phát triển và tính linh hoạt.

 
[Bình luận này đã bị ẩn.]
 
9vvin 2025-02-25

Tôi thích tương lai của Next.js: nó đang trở nên thật sự rất tuyệt

Từ sau Next.js 13, nó đã trở nên khá dùng được, nhưng dạo gần đây tôi thật sự rất rất thích nó. Có vẻ nó sẽ trở thành tiêu chuẩn thực tế của full-stack web development.