Next.js - 动态 API 路由
Next.js - 动态路由与数据获取
Section titled “Next.js - 动态路由与数据获取”动态路由(Dynamic routes)允许你根据动态数据创建页面,例如博客文章、产品详情或用户个人资料等,这些页面的 URL 结构取决于参数。
在 App Router 中,动态段(dynamic segments)通过在文件夹名称中使用方括号 [] 来定义。例如,app/posts/[slug]/page.tsx 会创建诸如 /posts/my-first-post、/posts/learning-nextjs 等路由。
示例:创建动态博客文章页面
Section titled “示例:创建动态博客文章页面”让我们构建一个简单的博客,其中每篇文章都基于唯一的 ‘slug’ 拥有自己的页面。
步骤 1:定义数据源
创建一个工具文件来模拟获取博客文章数据。创建 lib/posts.ts(如果项目根目录中没有 lib 目录,请创建它):
export interface Post { slug: string; title: string; content: string; date: string;}
// 模拟数据库或 API 调用const posts: Post[] = [ { slug: 'getting-started-with-nextjs', title: 'Getting Started with Next.js', content: 'Next.js is a React framework for production...', date: '2024-01-10', }, { slug: 'dynamic-routing-explained', title: 'Dynamic Routing Explained', content: 'Learn how dynamic routes work in the App Router...', date: '2024-01-15', },];
// 获取所有可能 slug 的函数(用于静态生成)export async function getAllPostSlugs(): Promise<{ slug: string }[]> { // 在实际应用中,从 CMS 或数据库中获取此数据 return posts.map(post => ({ slug: post.slug }));}
// 按 slug 获取文章数据的函数export async function getPostData(slug: string): Promise<Post | null> { // 在实际应用中,从 CMS 或数据库中获取此数据 const post = posts.find(p => p.slug === slug); return post || null;}步骤 2:创建动态页面组件
创建文件 app/posts/[slug]/page.tsx。文件夹名称 [slug] 表示 slug 是一个动态参数。
import { getPostData, getAllPostSlugs } from '@/lib/posts'; // 使用路径别名 '@'import { notFound } from 'next/navigation';import { Metadata } from 'next';
interface PostPageProps { params: { slug: string; // 动态段的值 };}
// 动态生成元数据的函数export async function generateMetadata({ params }: PostPageProps): Promise<Metadata> { const post = await getPostData(params.slug); if (!post) { return { title: 'Post Not Found' }; } return { title: post.title };}
// 在构建时生成静态路径的函数export async function generateStaticParams() { const slugs = await getAllPostSlugs(); // 返回一个数组,例如:[{ slug: 'post-1' }, { slug: 'post-2' }] return slugs;}
// 页面组件export default async function PostPage({ params }: PostPageProps) { const post = await getPostData(params.slug);
// 处理找不到文章数据的情况 if (!post) { notFound(); // 渲染最近的 not-found.tsx 页面或默认的 404 页面 }
return ( <article> <h1>{post.title}</h1> <p><time dateTime={post.date}>{post.date}</time></p> <div>{post.content}</div> {/* 如果需要,添加一个返回链接 */} {/* <Link href="/posts">Back to Posts List</Link> */} </article> );}**[slug]文件夹:** 定义了一个名为slug的动态路由段(dynamic route segment)。**paramsProp:** 页面组件接收一个paramsprop,其中包含当前请求的动态段的实际值(例如{ slug: ‘getting-started-with-nextjs’ }`)。**generateStaticParams():** (可选,但建议用于静态生成)此函数告诉 Next.js 在构建时存在哪些可能的slug值。Next.js 将为该函数返回的每个路径预渲染一个静态 HTML 页面。如果用户请求了此处未生成的路径,Next.js 可能会尝试按需生成或显示 404,具体取决于配置(layout.js或page.js中的dynamicParams选项,默认为true`)。**数据获取:**PostPage组件是一个服务器组件(Server Component),因此它直接使用await getPostData(params.slug)获取数据。无需像 Pages Router 中那样使用getStaticProps或getServerSideProps`。**notFound():** 来自next/navigation` 的一个辅助函数,用于优雅地处理请求的 slug 数据不存在的情况,渲染 404 页面。**generateMetadata()`:** (可选)允许基于获取的数据动态生成页面元数据(如标题、描述)。
启动开发服务器
Section titled “启动开发服务器”运行开发服务器:
npm run dev-
在浏览器中打开
http://localhost:3000/posts/getting-started-with-nextjs。你应该能看到第一篇博客文章的内容。 -
打开
http://localhost:3000/posts/dynamic-routing-explained。你应该能看到第二篇博客文章的内容。 -
尝试导航到不存在的 slug,例如
http://localhost:3000/posts/invalid-slug。你应该会看到一个 404 未找到页面(如果你创建了自定义的not-found.tsx,则显示该页面,否则显示默认的 Next.js 404 页面)。