Bài 28.2: Pages và routing trong Next.js

Chào mừng các bạn trở lại với chuỗi bài viết về Lập trình Web Front-end! Hôm nay, chúng ta sẽ đi sâu vào một trong những tính năng cốt lõi và mạnh mẽ nhất của Next.js: cách nó quản lý các "Pages" (trang) và thiết lập hệ thống "Routing" (định tuyến) linh hoạt, hiệu quả.

Nếu bạn đã từng làm việc với các ứng dụng React truyền thống (kiểu SPA với Create React App), bạn có thể đã quen thuộc với việc cài đặt và cấu hình các thư viện routing như react-router-dom. Next.js mang đến một cách tiếp cận hoàn toàn khác, đơn giản hơn nhiều nhưng cực kỳ mạnh mẽ: file-system routing.

Pages: Nền tảng của ứng dụng Next.js

Trong Next.js, một Page đơn giản là một React Component được export mặc định (default export) từ một file nằm trong thư mục đặc biệt. Thư mục này, kể từ Next.js 13 trở đi, được khuyến khích là thư mục app.

Mỗi file trong thư mục app (hoặc các thư mục con của nó) có tên là page.tsx (hoặc .jsx, .js) sẽ tự động trở thành một trang trong ứng dụng của bạn.

  • Ví dụ đơn giản nhất: Trang chủ. Để tạo trang chủ (route /), bạn chỉ cần tạo một file tại app/page.tsx.

    // app/page.tsx
    import React from 'react';
    
    export default function HomePage() {
      return (
        <div>
          <h1>Chào mừng đến với Trang Chủ!</h1>
          <p>Đây  nội dung của trang chủ.</p>
        </div>
      );
    }
    

    Giải thích: File app/page.tsx được đặt tại thư mục gốc của app. Theo quy ước của Next.js, file page.tsx ở cấp cao nhất này sẽ tương ứng với URL gốc / của trang web.

Routing: Sức mạnh từ cấu trúc file

Điểm thú vịkhác biệt của Next.js chính là cách nó xử lý routing. Thay vì cấu hình tuyến đường trong một file tập trung, Next.js sử dụng cấu trúc thư mục và tên file bên trong thư mục app để định nghĩa các tuyến đường URL.

  • Ví dụ về Static Routing: Nếu bạn muốn tạo một trang "Giới thiệu" với URL /about, bạn chỉ cần tạo một thư mục con about bên trong app và đặt file page.tsx vào đó.

    // app/about/page.tsx
    import React from 'react';
    
    export default function AboutPage() {
      return (
        <div>
          <h1>Giới thiệu về chúng tôi</h1>
          <p>Đây  trang giới thiệu.</p>
        </div>
      );
    }
    

    Giải thích: Cấu trúc thư mục app/about/ tương ứng với URL /about. File page.tsx bên trong thư mục đó là component sẽ được render khi người dùng truy cập /about. Thật đơn giản phải không nào?

    Bạn có thể tạo các cấp độ lồng nhau tương tự:

    • app/products/page.tsx -> /products
    • app/dashboard/settings/page.tsx -> /dashboard/settings

    Cấu trúc file trực quan hóa cấu trúc URL, giúp bạn dễ dàng tổ chức dự án và hiểu được các tuyến đường mà không cần nhìn vào file cấu hình routing riêng biệt.

Điều hướng giữa các Pages với <Link>

Trong các ứng dụng React truyền thống, bạn thường dùng thẻ <a> để tạo liên kết. Tuy nhiên, thẻ <a> thông thường sẽ gây ra tải lại toàn bộ trang (full page reload) khi chuyển hướng, làm mất đi trải nghiệm mượt mà của ứng dụng một trang (SPA).

Next.js cung cấp component <Link> từ thư viện next/link để xử lý việc này một cách thông minh. <Link> cho phép chuyển hướng giữa các trang trong ứng dụng của bạn mà không cần tải lại toàn bộ trang, mang lại trải nghiệm người dùng nhanh chóng và liền mạch giống như một SPA thực thụ.

  • Ví dụ sử dụng <Link>: Để thêm liên kết từ trang chủ đến trang giới thiệu, bạn chỉnh sửa file app/page.tsx như sau:

    // app/page.tsx
    import React from 'react';
    import Link from 'next/link'; // Import component Link
    
    export default function HomePage() {
      return (
        <div>
          <h1>Chào mừng đến với Trang Chủ!</h1>
          <p>Đây  nội dung của trang chủ.</p>
          <nav>
            {/* Sử dụng component Link để điều hướng */}
            <Link href="/about">
              Đi đến trang Giới thiệu
            </Link>
            {/* <a href="/about">Sẽ tải lại trang</a> */}
          </nav>
        </div>
      );
    }
    

    Giải thích:

    • Chúng ta import Link từ next/link.
    • Component <Link> nhận prop href là đường dẫn nội bộ của trang muốn đến (giống như href của thẻ <a>).
    • Nội dung bên trong <Link> (ở đây là <Link href="/about">Đi đến trang Giới thiệu</Link>) sẽ là element mà người dùng tương tác (ví dụ: văn bản, button, hình ảnh). Component <Link> sẽ render ra thẻ <a> thực tế với href tương ứng, nhưng sẽ xử lý việc click để thực hiện chuyển hướng client-side thay vì tải lại trang.

Next.js cũng hỗ trợ prefetching (tải trước) nội dung của các trang được liên kết khi chúng xuất hiện trong viewport, giúp việc chuyển trang diễn ra gần như tức thời.

Dynamic Routes: Xử lý các URL thay đổi

Không phải lúc nào các URL cũng cố định. Ví dụ, bạn có thể cần hiển thị thông tin chi tiết của một bài viết dựa trên ID hoặc slug của nó (ví dụ: /posts/bai-viet-a, /posts/bai-viet-b). Đây là lúc Dynamic Routes phát huy tác dụng.

Trong Next.js, bạn tạo dynamic route bằng cách sử dụng dấu ngoặc vuông [] trong tên thư mục. Tên bên trong dấu ngoặc vuông (ví dụ: [slug]) sẽ trở thành tên tham số mà bạn có thể truy cập trong component trang của mình.

  • Ví dụ về Dynamic Route: Để tạo trang hiển thị chi tiết bài viết theo slug (ví dụ: /blog/ten-bai-viet), bạn tạo cấu trúc thư mục app/blog/[slug]/page.tsx.

    // app/blog/[slug]/page.tsx
    import React from 'react';
    
    // Next.js sẽ tự động truyền params vào component page
    export default function BlogPostPage({ params }: { params: { slug: string } }) {
      const postSlug = params.slug;
    
      // Ở đây, bạn có thể fetch dữ liệu bài viết dựa trên postSlug
      // Ví dụ: const postData = await fetchPostBySlug(postSlug);
    
      return (
        <div>
          <h1>Chi tiết bài viết</h1>
          <p>Slug của bài viết này : *_{postSlug}_*</p>
          {/* Render nội dung bài viết dựa trên postData */}
        </div>
      );
    }
    
    // (Tùy chọn) Hàm này giúp tạo các trang tĩnh cho các slug cụ thể
    // Rất hữu ích cho các bài viết không thay đổi thường xuyên
    export async function generateStaticParams() {
        // Giả sử bạn có danh sách các slugs bài viết từ API hoặc database
        const slugs = ['bai-viet-a', 'bai-viet-b', 'mot-bai-khac']; // Ví dụ
    
        return slugs.map((slug) => ({
            slug: slug,
        }));
    }
    

    Giải thích:

    • Thư mục [slug] bên trong app/blog/ cho Next.js biết rằng bất kỳ URL nào khớp với pattern /blog/* (ngoại trừ các route cố định khác trong /blog) sẽ được xử lý bởi component page.tsx này.
    • Tên bên trong dấu ngoặc vuông (slug) trở thành một key trong object params được truyền vào component page. Bạn có thể truy cập giá trị thực tế từ URL thông qua params.slug.
    • Hàm generateStaticParams (chỉ có trong App Router) là một tính năng mạnh mẽ. Nó cho phép bạn định nghĩa trước các giá trị params (ở đây là các slug) mà Next.js sẽ sử dụng để pre-render (tạo tĩnh) các trang động này tại thời điểm build. Điều này giúp cải thiện hiệu suất và SEO đáng kể cho các trang có nội dung tĩnh hoặc ít thay đổi.

Bạn có thể có nhiều dynamic segments trong một route (ví dụ: app/shop/[category]/[productId]/page.tsx sẽ khớp với /shop/electronics/laptop-123).

Tóm lại

Hệ thống PagesRouting dựa trên cấu trúc file của Next.js là một cách tiếp cận độc đáohiệu quả để xây dựng các ứng dụng web hiện đại.

  • Mỗi file page.tsx (hoặc .jsx, .js) trong thư mục app (hoặc thư mục con) đại diện cho một trang tại một URL cụ thể.
  • Cấu trúc thư mục trong app trực tiếp ánh xạ tới cấu trúc URL.
  • Sử dụng component <Link> từ next/link để điều hướng giữa các trang một cách mượt màhiệu quả (client-side navigation).
  • Dynamic Routes sử dụng cú pháp [] trong tên thư mục để xử lý các URL có chứa các tham số thay đổi (ví dụ: /blog/[slug]).

Nắm vững khái niệm Pages và Routing là chìa khóa để xây dựng các ứng dụng Next.js có tổ chức, dễ bảo trì và đạt hiệu suất cao nhờ vào khả năng rendering mạnh mẽ của Next.js. Hãy thử nghiệm tạo các trang và liên kết khác nhau trong dự án Next.js của bạn để làm quen với cách hoạt động này nhé!

Comments

There are no comments at the moment.