Next.js / Dynamic маршрут

Dynamic маршрут

Бодит апп дахь хуудас бүр статик биш — /courses/javascript, /courses/react, /courses/python гэх мэт олон хуудас нэг загвараас үүсдэг. Next.js-д үүнийг dynamic route ашиглан хялбархан шийддэг. Энэ хичээлд dynamic маршрутыг дэлгэрэнгүй судална.

[slug] параметр

Директорын нэрийг хаалтанд бичихэд тухайн сегмент dynamic болно:

код
app/courses/[courseSlug]/page.tsx

Энэ нэг файл дараах бүх хаягт зориулан ажиллана:

код
/courses/javascript   →  courseSlug = "javascript"
/courses/react        →  courseSlug = "react"
/courses/python       →  courseSlug = "python"
/courses/go           →  courseSlug = "go"

params prop-оор параметрийн утгыг авна. Next.js 15-д params бол Promise тул заавал await хийнэ:

tsx
// app/courses/[courseSlug]/page.tsx
interface Props {
  params: Promise<{ courseSlug: string }>;
}

export default async function CoursePage({ params }: Props) {
  const { courseSlug } = await params;

  return (
    <main>
      <h1>{courseSlug} курс</h1>
    </main>
  );
}

Бодит жишээ — курсын хуудас

Файл системээс курсын мэдээллийг уншиж харуулах жишээ:

tsx
// app/courses/[courseSlug]/page.tsx
import { notFound } from "next/navigation";
import { getCourse } from "@/lib/courses";
import type { Metadata } from "next";

interface Props {
  params: Promise<{ courseSlug: string }>;
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { courseSlug } = await params;
  const course = await getCourse(courseSlug);

  if (!course) return { title: "Олдсонгүй" };

  return {
    title: `${course.title} | ulaanbaatar.app`,
    description: course.description,
  };
}

export default async function CoursePage({ params }: Props) {
  const { courseSlug } = await params;
  const course = await getCourse(courseSlug);

  if (!course) {
    notFound(); // 404 хуудас руу шилжүүлнэ
  }

  return (
    <main className="p-6">
      <h1 className="text-3xl font-bold text-white">{course.title}</h1>
      <p className="mt-2 text-slate-400">{course.description}</p>
      <ul className="mt-6 space-y-2">
        {course.lessons.map((lesson) => (
          <li key={lesson.slug}>
            <a
              href={`/courses/${courseSlug}/${lesson.slug}`}
              className="text-indigo-400 hover:underline"
            >
              {lesson.order}. {lesson.title}
            </a>
          </li>
        ))}
      </ul>
    </main>
  );
}

Nested dynamic параметр

Курс болон хичээл хоёуланг dynamic байлгахад nested параметр ашиглана:

код
app/courses/[courseSlug]/[lessonSlug]/page.tsx
tsx
// app/courses/[courseSlug]/[lessonSlug]/page.tsx
import { notFound } from "next/navigation";
import { getCourse, getLesson } from "@/lib/courses";

interface Props {
  params: Promise<{
    courseSlug: string;
    lessonSlug: string;
  }>;
}

export default async function LessonPage({ params }: Props) {
  const { courseSlug, lessonSlug } = await params;

  const course = await getCourse(courseSlug);
  const lesson = await getLesson(courseSlug, lessonSlug);

  if (!course || !lesson) {
    notFound();
  }

  return (
    <article className="prose prose-invert max-w-3xl mx-auto p-6">
      <p className="text-slate-400 text-sm">
        {course.title} / {lesson.title}
      </p>
      <h1 className="text-white">{lesson.title}</h1>
      {/* Хичээлийн агуулга энд */}
    </article>
  );
}

generateStaticParams — урьдчилан үүсгэх

Production build хийхэд Next.js dynamic хуудсуудыг урьдчилан статик HTML болгон үүсгэж болно — энэ нь хуудсыг маш хурдан болгодог. generateStaticParams функц нь бүх боломжит параметрүүдийн жагсаалтыг буцаана:

tsx
// app/courses/[courseSlug]/page.tsx-д нэмнэ
import { getAllCourses } from "@/lib/courses";

export async function generateStaticParams() {
  const courses = await getAllCourses();

  return courses.map((course) => ({
    courseSlug: course.slug,
  }));
}
// Next.js build хийхэд /courses/javascript, /courses/react г.м.
// хуудас бүрийг автоматаар HTML болгон үүсгэнэ

Nested dynamic параметртэй хуудасд ийм байна:

tsx
// app/courses/[courseSlug]/[lessonSlug]/page.tsx-д нэмнэ
export async function generateStaticParams() {
  const courses = await getAllCourses();

  const params = [];
  for (const course of courses) {
    for (const lesson of course.lessons) {
      params.push({
        courseSlug: course.slug,
        lessonSlug: lesson.slug,
      });
    }
  }
  return params;
}

notFound() ашиглах

Dynamic хуудасд байхгүй slug ирвэл notFound() функцийг дуудна. Энэ нь app/not-found.tsx хуудсыг харуулна:

tsx
// app/not-found.tsx
import Link from "next/link";

export default function NotFoundPage() {
  return (
    <main className="flex flex-col items-center justify-center min-h-screen">
      <h1 className="text-4xl font-bold text-white">404</h1>
      <p className="mt-2 text-slate-400">Хуудас олдсонгүй</p>
      <Link href="/" className="mt-4 text-indigo-400 hover:underline">
        Нүүр хуудас руу буцах
      </Link>
    </main>
  );
}

Дараагийн хичээлд:

Catch-all болон optional маршрутыг судална. [...slug] ба [[...slug]] синтакс ашиглан нэг хуудсаар олон түвшний замыг барих аргыг ойлгоно.