6月2日 23:16

Next.js App Router 怎么定义路由?动态路由、布局嵌套和 loading 详解

Next.js App Router 用文件系统定义路由——文件夹结构就是 URL 结构。每个路由三要素:page.tsx(页面)、layout.tsx(布局)、loading.tsx(加载状态)。

基本路由映射

shell
app/ ├── page.tsx → / ├── about/ │ └── page.tsx → /about ├── blog/ │ ├── page.tsx → /blog │ └── [slug]/ │ └── page.tsx → /blog/:slug(动态路由) ├── dashboard/ │ ├── layout.tsx → dashboard 共享布局 │ ├── page.tsx → /dashboard │ └── settings/ │ └── page.tsx → /dashboard/settings

page.tsx 是必须的——没有 page.tsx 的文件夹不构成路由。其他文件(layout、loading、error)都是可选的。

动态路由

tsx
// app/blog/[slug]/page.tsx export default function BlogPost({ params }: { params: { slug: string } }) { return <h1>文章: {params.slug}</h1>; }

访问 /blog/hello-world 时,params.slug = "hello-world"

Catch-all 路由用 [...slug]:匹配 /shop/clothes/shirts 这样的多级路径,params.slug = ["clothes", "shirts"]

路由组

(groupName) 创建不反映在 URL 中的分组:

shell
app/ ├── (marketing)/ │ ├── about/page.tsx → /about(URL 里没有 marketing) │ └── pricing/page.tsx → /pricing ├── (shop)/ │ ├── products/page.tsx → /products │ └── cart/page.tsx → /cart

路由组的用途:给一组页面共享 layout 而不影响 URL。比如 (marketing) 组用营销页布局,(shop) 组用商店布局。

布局嵌套

layout.tsx 会嵌套——子路由的 layout 包在父 layout 里面:

tsx
// app/layout.tsx — 根布局(必有,包含 html/body) export default function RootLayout({ children }) { return ( <html> <body> <nav>全局导航</nav> {children} </body> </html> ); } // app/dashboard/layout.tsx — dashboard 布局 export default function DashboardLayout({ children }) { return ( <div className="flex"> <aside>侧边栏</aside> <main>{children}</main> </div> ); }

从 /dashboard 切换到 /dashboard/settings 时,根布局和 dashboard 布局都不会重新渲染。只有 children 对应的 page.tsx 更新。

loading.tsx:自动 Suspense

tsx
// app/dashboard/loading.tsx export default function Loading() { return <div className="animate-pulse">加载中...</div>; }

Next.js 自动用 Suspense 包裹 page.tsx,加载时显示 loading.tsx。不需要手写 useState 管理加载状态。

error.tsx:错误边界

tsx
// app/error.tsx — 必须是 Client Component 'use client' export default function Error({ error, reset }) { return ( <div> <p>出错了: {error.message}</p> <button onClick={reset}>重试</button> </div> ); }

error.tsx 捕获子组件的运行时错误,显示错误界面。reset 函数重新渲染出错的组件。

程序化导航

tsx
'use client' import { useRouter } from 'next/navigation'; function LoginButton() { const router = useRouter(); return <button onClick={() => router.push('/dashboard')}>登录</button>; }

router.push() 客户端跳转,router.replace() 替换当前历史记录(不能回退),router.back() 返回上一页。

Server Component 里用 redirect()

tsx
import { redirect } from 'next/navigation'; export default async function Page() { const session = await getSession(); if (!session) redirect('/login'); // ... }

redirect 在服务端执行,用户看不到中间页面。

标签:Next.js