Next.js App Router toàn tập: Làm chủ Kiến trúc thư mục và Advanced Routing
Khi Next.js tung ra phiên bản 13 với thư mục app/, cộng đồng dev đã chia làm hai nửa: Một nửa vỗ tay tán thưởng vì hiệu năng quá khủng, nửa còn lại thì "tẩu hỏa nhập ma" vì cấu trúc thư mục quá lắt léo và khác biệt.
Thực chất, App Router không hề làm khó bạn. Nó ép bạn phải thiết kế ứng dụng theo một kiến trúc module hóa chặt chẽ hơn. Khác với pages/ (nơi mà cứ tạo file là thành URL), thư mục app/ mang đến quyền kiểm soát định tuyến tinh tế hơn rất nhiều.
1. Nguyên tắc cốt lõi: Colocation (Đặt code ở nơi nó thuộc về)
Ở thế hệ cũ (pages/), nếu bạn lỡ tay tạo một file components/Button.tsx bên trong thư mục pages, Next.js sẽ ngớ ngẩn biến nó thành cái URL domain.com/components/Button. Điều này buộc bạn phải vứt toàn bộ component, lib, utils ra một thư mục rác bên ngoài.
App Router giải quyết triệt để sự ngu ngốc này bằng quy tắc: Chỉ khi nào một thư mục chứa file có tên chính xác là page.tsx (hoặc .jsx, .js), thì thư mục đó mới được phép công khai ra public thành một URL. Mọi file khác (Button.tsx, api.ts, styles.css) dù nằm chung thư mục cũng hoàn toàn "tàng hình" trước người dùng.
Điều này cho phép bạn gom toàn bộ code liên quan đến một tính năng vào chung một chỗ (Colocation):
Plaintext
app/
└── dashboard/
├── page.tsx # Trở thành URL: /dashboard
├── layout.tsx # UI bọc quanh dashboard
├── Button.tsx # Tàng hình (Chỉ dùng nội bộ)
└── data-fetch.ts # Tàng hình (Chỉ dùng nội bộ)
2. Ma trận Special Files (Các file hệ thống)
Ngoài page.tsx, hệ sinh thái App Router định nghĩa một loạt các file đặc biệt, được thiết kế để bao bọc lẫn nhau theo kiến trúc phân tầng (Hierarchical).
layout.tsx(Bộ khung bất biến): Là phần UI không bao giờ bị render lại khi bạn chuyển trang bên trong nó (Ví dụ: Sidebar, Navbar).template.tsx: Giống hệt Layout, nhưng khác ở chỗ: Mỗi khi chuyển trang, nó sẽ bị hủy và tạo lại từ đầu (Thích hợp để làm hiệu ứng Animation chuyển trang hoặc reset State).loading.tsx: Dùng React Suspense để hiển thị Skeleton/Spinner ngay lập tức trong lúc trang con đang gọi API.error.tsx&global-error.tsx: Bộ chắn lỗi cấp thư mục. Code ở trang A bị crash sẽ bị nhốt lại ởerror.tsxcủa trang A, không làm sập toàn bộ app.not-found.tsx: Giao diện lỗi 404 tùy chỉnh.route.ts: Thay thế cho thư mụcpages/apicũ. Dùng để viết Backend API (RESTful) ngay trong Next.js. Lưu ý: Không thể đặtroute.tscùng cấp thư mục vớipage.tsxvì chúng sẽ đánh nhau!
3. Cây thư mục thực chiến & Các Pattern định tuyến nâng cao
Phần này là "tinh hoa" của App Router. Vercel cung cấp các ký tự đặc biệt trên tên thư mục để làm phép thuật định tuyến. Hãy xem cây thư mục tổng hợp dưới đây:
Plaintext
app/
├── (marketing)/ # 1. Route Group (Ngoặc đơn)
│ ├── about/page.tsx # URL: /about
│ └── contact/page.tsx # URL: /contact
├── blog/
│ ├── [slug]/page.tsx # 2. Dynamic Route (Ngoặc vuông)
│ └── [...catchAll]/page.tsx # 3. Catch-all Route
├── @modal/ # 4. Parallel Route (Dấu @)
│ └── default.tsx
└── layout.tsx # Root Layout bắt buộc
3.1. Route Groups - Dấu ngoặc đơn (folder)
Bạn muốn gom nhóm thư mục about và contact vào chung một cụm để xài chung một cái layout.tsx riêng (layout cho marketing), nhưng bạn lại không muốn URL bị biến thành domain.com/marketing/about.
$\rightarrow$ Chỉ cần bọc tên thư mục trong ngoặc đơn (marketing). Next.js sẽ hiểu đây là thư mục gom nhóm và tàng hình nó khỏi URL.
3.2. Dynamic & Catch-all Routes - Dấu ngoặc vuông [id]
app/blog/[slug]/page.tsxsẽ hứng các URL như/blog/bai-viet-1,/blog/bai-viet-2. Biếnslugsẽ được truyền vào component dưới dạng params.app/shop/[...slug]/page.tsx(Thêm 3 dấu chấm) sẽ hứng toàn bộ mọi chuỗi URL phía sau. Ví dụ:/shop/ao-thun/mau-do/size-m. Rất thích hợp làm trang bộ lọc sản phẩm phức tạp.
3.3. Parallel Routes - Dấu A còng @folder
Bạn muốn render cùng lúc nhiều component khổng lồ (với logic loading và error riêng biệt) trên cùng một URL duy nhất (ví dụ: Trang Dashboard chứa Bảng thống kê và Lịch làm việc)?
$\rightarrow$ Tạo các thư mục bắt đầu bằng @. Trong file layout.tsx tổng, bạn có thể nhận chúng dưới dạng props và sắp xếp vị trí song song với nhau.
3.4. Intercepting Routes - Dấu chấm (..)folder
Đây là tính năng làm Modal đỉnh nhất. Giả sử bạn đang cuộn bảng tin (Feed) tại /photo và click vào một bức ảnh /photo/123.
Nếu click từ Feed: Next.js "chặn" luồng đi lại, bật một cái Modal đè lên trang hiện tại (dùng Intercepting Route).
Nhưng nếu copy URL
/photo/123gửi cho bạn bè: Mở ra sẽ là nguyên một trang web to đùng hiển thị bức ảnh đó.
4. Server Components: Cú đấm thép vào hiệu năng
Sau khi đã quy hoạch xong cấu trúc thư mục, điều quan trọng nhất bạn phải nhớ: Mọi file page.tsx và layout.tsx mặc định đều là React Server Components (RSC).
Tức là:
Bạn có thể chọc thẳng vào Database (Prisma, MongoDB), hoặc gọi
fetch()bằngasync/awaitngay bên trong Component.Code này chạy ở Server, kết quả nướng thành HTML gửi về Client. Trình duyệt không tốn 1 byte RAM nào để tải thư viện (như
moment.jshayaxios) mà bạn import trên đây.Nếu bạn cần xài các hook thao tác với giao diện (
useState,useEffect,onClick), bạn MỚI thêm dòng"use client"lên đầu file. Lúc này nó trở thành Component trình duyệt bình thường.
5. Lời kết
Kiến trúc thư mục app/ của Next.js không chỉ sinh ra để cho đẹp. Nó là bản thiết kế (Blueprint) phản ánh chính xác tư duy hệ thống: Chia nhỏ giao diện, tối ưu loading state, quản lý luồng dữ liệu song song và đẩy tối đa khối lượng tính toán về phía Server. Làm chủ được cây thư mục này, bạn có thể tự tin kiến trúc những dự án Web Enterprise quy mô lớn.