Giới thiệu

NextJS 15 mang lại khả năng SSR và ISR mạnh mẽ. Kết hợp TypeScript giúp giảm lỗi runtime, tăng tính bảo trì. Bài viết chia sẻ cách cấu hình, tối ưu, và tránh bẫy hiệu năng.

SSR vs ISR

SSR cơ bản

SSR tạo HTML mỗi lần yêu cầu. Dùng getServerSideProps để fetch dữ liệu trên server.

export async function getServerSideProps(context) {
  const res = await fetch(`https://api.example.com/posts/${context.params.id}`);
  const data = await res.json();
  return { props: { post: data } };
}

Ưu điểm: dữ liệu luôn mới. Nhược điểm: thời gian phản hồi tăng khi server tải.

ISR cấu hình

ISR cho phép tái tạo trang tĩnh sau khoảng thời gian định trước, kết hợp lợi thế của SSG và cập nhật dữ liệu.

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();
  return {
    props: { posts },
    revalidate: 60 // tái tạo mỗi 60 giây
  };
}

export async function getStaticPaths() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();
  const paths = posts.map(p => ({ params: { id: p.id.toString() } }));
  return { paths, fallback: 'blocking' };
}

Chiến lược: dùng fallback: 'blocking' để render trang chưa có trên demand, tránh 404.

Tối ưu TypeScript

Định nghĩa kiểu dữ liệu trả về giúp IDE gợi ý chính xác và giảm bug.

type Post = {
  id: number;
  title: string;
  content: string;
  author: { id: number; name: string };
};

export async function getStaticProps(): Promise<{ props: { posts: Post[] } }> {
  const res = await fetch('https://api.example.com/posts');
  const posts: Post[] = await res.json();
  return { props: { posts } };
}

Sử dụng inferReturnType để tái sử dụng kiểu trong nhiều component.

type Props = ReturnType['props'];

function PostList({ posts }: Props) {
  return (
    
    {posts.map(p => (
  • {p.title}
  • ))}
); }

Hiệu năng và cache

Áp dụng Cache‑Control header trong middleware để giảm tải CDN.

export function middleware(request) {
  const response = NextResponse.next();
  response.headers.set('Cache-Control', 'public, max-age=60, stale-while-revalidate=30');
  return response;
}

Kết hợp SWR ở client để tái fetch khi dữ liệu thay đổi mà không làm gián đoạn UI.

import useSWR from 'swr';

function PostDetail({ initialData }) {
  const { data } = useSWR('/api/posts/1', fetcher, { fallbackData: initialData });
  return 
{data?.title}
; }

Kết luận

SSR phù hợp với nội dung luôn thay đổi, ISR tối ưu cho danh sách tĩnh có cập nhật định kỳ. TypeScript bảo vệ kiểu, giảm lỗi, tăng tốc phát triển. Tham khảo khóa học "Lập trình Front-End với NextJS + TypeScript" tại đây.