Giới Thiệu
Next.js 13.4 đã giới thiệu App Router như một bước tiến lớn so với Pages Router truyền thống. Được xây dựng trên React Server Components (RSC), kiến trúc mới này không chỉ cải thiện hiệu suất mà còn cho phép các mẫu UI phức tạp hơn, đồng thời hoàn toàn làm mới trải nghiệm của nhà phát triển.
Trong bài viết này, chúng ta sẽ tìm hiểu về sự so sánh giữa Pages Router và App Router, các quy tắc file, Suspense và Streaming, Parallel Routes và Intercepting Routes, thành phần <Link>, và middleware—tất cả những điểm thực tiễn mà bạn cần biết.
1. So Sánh: Pages Router và App Router
Hãy bắt đầu bằng việc so sánh Pages Router kế thừa với App Router mới để có cái nhìn tổng quát.
| Tính Năng | Pages Router | App Router |
|---|---|---|
| Thư mục | pages/ |
app/ |
| Loại Thành Phần | Thành phần Client theo mặc định | Thành phần Server theo mặc định (sử dụng use client để chuyển đổi) |
| Lấy Dữ Liệu | getStaticProps / getServerSideProps |
Lời gọi fetch trực tiếp trên server |
| Layouts | _app.tsx / _document.tsx |
layout.tsx (hỗ trợ layout lồng nhau) |
| Định Tuyến | Phẳng, dựa trên file | Dựa trên cấu trúc thư mục, hỗ trợ Nhóm Tuyến |
| Giao Diện Bị Trì Hoãn | Triển khai thủ công | Suspense + loading.tsx + Streaming |
| Xử Lý Lỗi | _error.tsx / 404.tsx |
error.tsx / not-found.tsx |
| Đường Dẫn API | pages/api/*.ts |
route.ts |
| UI Phức Tạp / Modal | Triển khai với logic trạng thái và điều kiện | Hỗ trợ chính thức với Parallel Routes / Intercepting Routes |
| SEO / Metadata | next/head |
API metadata với tính an toàn về kiểu |
👉 App Router tiến tới sự phân tách rõ ràng về trách nhiệm và hỗ trợ chính thức cho các mẫu UI phức tạp.
2. Quy Tắc File trong App Router
Trong App Router, việc đặt các file đặc biệt trong thư mục app/ sẽ xác định định tuyến và hành vi UI.
| File | Mục Đích |
|---|---|
page.tsx |
Thành phần trang chính, ánh xạ đến URL. |
layout.tsx |
Layout chia sẻ. Lồng nhau và duy trì trong các chuyển tiếp trang. |
template.tsx |
Tương tự như layout.tsx nhưng sẽ tái mount khi điều hướng (ví dụ: đặt lại form). |
loading.tsx |
UI tải trong quá trình lấy dữ liệu. Tự động làm việc với Suspense. |
error.tsx |
Ranh giới lỗi cho tuyến đường. |
not-found.tsx |
UI 404 tùy chỉnh cho tuyến đường. |
route.ts |
Xác định các đường dẫn API bằng cách xuất các hàm cho các phương thức HTTP. |
default.tsx |
UI mặc định cho Parallel Routes khi không có gì được chọn. |
Thư mục (group) |
Nhóm cho mục đích tổ chức mà không ảnh hưởng đến URL. |
Thư mục [param] |
Các tuyến đường động, ví dụ: /users/[id]. |
👉 Khả năng tách biệt xử lý tải, lỗi và không tìm thấy theo từng tuyến đường là một bước tiến lớn về độ rõ ràng và khả năng bảo trì.
3. Suspense và Streaming
Suspense
<Suspense> hiển thị một UI dự phòng trong khi dữ liệu đang được lấy hoặc các thành phần đang được tải lười biếng.
tsx
<Suspense fallback={<p>Đang tải bình luận...</p>}>
<Comments />
</Suspense>
👉 Trong khi đang lấy dữ liệu, nó sẽ hiển thị “Đang tải bình luận...”, sau đó chuyển đổi liền mạch sang UI cuối cùng khi đã sẵn sàng.
Streaming
Streaming gửi HTML tới client ngay khi mỗi phần đã sẵn sàng, thay vì chờ đợi toàn bộ trang. Điều này loại bỏ nỗi lo “chờ màn hình trắng”, dẫn đến trải nghiệm người dùng mượt mà hơn.
4. Parallel Routes và Intercepting Routes
Parallel Routes
Cho phép hiển thị nhiều tuyến đường đồng thời.
Ví dụ: Trong một ứng dụng email, hiển thị “hộp thư đến” và “chi tiết” bên cạnh nhau.
tsx
// layout.tsx
export default function Layout({ inbox, detail }: { inbox: React.ReactNode, detail: React.ReactNode }) {
return (
<div className="grid grid-cols-2">
<div>{inbox}</div>
<div>{detail}</div>
</div>
);
}
Intercepting Routes
Chặn điều hướng và thay thế nó bằng một giao diện thay thế, chẳng hạn như một modal.
Ví dụ: Hiển thị trang chi tiết sản phẩm bên trong một modal khi nhấp vào.
👉 Điều này kết hợp điều hướng modal theo kiểu SPA với việc hiển thị toàn trang khi được truy cập trực tiếp.
5. Thành Phần <Link> và Prefetching
Thành phần <Link> cho phép điều hướng client-side nhanh chóng trong Next.js.
Cách Hoạt Động
- Khi một
<Link>vào viewport, Next.js prefetches tài nguyên cho trang đích. - Khi nhấp vào, các tài nguyên đã được tải trước sẽ được sử dụng ngay lập tức.
- Việc điều hướng trang hoàn tất gần như ngay lập tức.
👉 Nhờ prefetching, việc điều hướng cảm giác như chuyển trang tức thì.
6. Middleware
Tổng Quan
middleware.ts chạy ngay sau khi một yêu cầu đến server, trước khi render một trang hoặc tuyến API.
Các Trường Hợp Sử Dụng Thông Thường
- Kiểm tra xác thực: Chuyển hướng người dùng chưa xác thực đến
/login. - Phát hiện ngôn ngữ: Chuyển hướng dựa trên tiêu đề
Accept-Language. - Kiểm tra A/B: Chia lưu lượng giữa các biến thể.
- Kiểm soát truy cập: Chặn yêu cầu dựa trên IP hoặc user-agent.
Ví Dụ
ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const token = request.cookies.get("token");
if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*"],
};
👉 Điều này cho phép bạn thực thi kiểm tra xác thực và chuyển hướng trước khi bất kỳ logic trang nào chạy.
Kết Luận
- Những tiến bộ của App Router
- Layout lồng nhau, quy tắc file rõ ràng, và Streaming/Suspense cho trải nghiệm người dùng cải thiện.
- UI phức tạp được hỗ trợ chính thức với Parallel Routes và Intercepting Routes.
- Lợi ích của
<Link>- Prefetching cho phép điều hướng client-side cảm giác ngay lập tức.
- Sức mạnh của Middleware
- Thực hiện kiểm tra xác thực, chuyển hướng, và phát hiện ngôn ngữ ngay sau khi yêu cầu đến.
👉 Đối với các dự án mới, App Router được khuyến nghị mạnh mẽ. Đối với các dự án hiện tại, hãy di chuyển từng bước để tránh rủi ro.
Tài Nguyên Tham Khảo
- Cơ bản về App Router và cấu trúc file (Zenn)
- Bài viết về App Router trong Next.js (Zenn)