主题
05 · App Router 路由心智:layout / page / loading / error
目标:用文件系统配好多级路由与共享 layout,理解嵌套与导航,不再靠「Vue Router 配置表」硬套。
官方主文:Routing Fundamentals · Pages and Layouts · Linking and Navigating
1. 核心心智:文件夹 = 路由段
text
app/
layout.tsx → 所有页面共享根壳
page.tsx → /
notes/
layout.tsx → /notes 段共享壳(可选)
page.tsx → /notes
[id]/
page.tsx → /notes/:id
about/
page.tsx → /about1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
| 约定文件 | 作用 |
|---|---|
page.tsx | 该段可访问的 UI(没有 page 则不可当路由落地) |
layout.tsx | 包裹同段及子段;切换子页时 layout 可保持不卸载 |
loading.tsx | 该段加载时的即时 loading UI(Suspense 边界) |
error.tsx | 该段错误 UI(必须 Client Component) |
not-found.tsx | 404 UI |
对照 Vue Router:routes 表 → 目录树;router-view 嵌套 → layout 嵌套 children(由框架拼)。
2. 手敲:笔记区路由骨架
在 02 项目中创建:
app/notes/layout.tsx
tsx
import Link from 'next/link'
export default function NotesLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<section style={{ padding: 24 }}>
<header style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
<strong>Notes</strong>
<Link href="/notes">列表</Link>
<Link href="/">回首页</Link>
</header>
{children}
</section>
)
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app/notes/page.tsx
tsx
import Link from 'next/link'
const DEMO = [
{ id: '1', title: '第一篇' },
{ id: '2', title: '第二篇' },
]
export default function NotesPage() {
return (
<main>
<h1>笔记列表</h1>
<ul>
{DEMO.map((n) => (
<li key={n.id}>
<Link href={`/notes/${n.id}`}>{n.title}</Link>
</li>
))}
</ul>
</main>
)
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
app/notes/[id]/page.tsx
tsx
type Props = { params: Promise<{ id: string }> }
export default async function NoteDetailPage({ params }: Props) {
const { id } = await params
return (
<main>
<h1>笔记详情</h1>
<p>id: {id}</p>
</main>
)
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
params在较新 Next 版本为 Promise,需await(以你安装的 nextjs.org 当前文档为准;若类型是同步对象,按官方该版写法即可)。
app/notes/loading.tsx
tsx
export default function Loading() {
return <p>加载笔记中…</p>
}1
2
3
2
3
app/notes/error.tsx
tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<p>出错了:{error.message}</p>
<button type="button" onClick={reset}>
重试
</button>
</div>
)
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
访问 /notes、/notes/1,确认 layout 里的导航在子页间保留。
3. 导航:用 Link,少硬跳
tsx
import Link from 'next/link'
import { useRouter } from 'next/navigation' // 仅 Client Component1
2
2
| 场景 | 做法 |
|---|---|
| 声明式跳转 | <Link href="..."> |
| 事件后跳转 | Client 里 router.push |
| 外链 | 普通 <a> |
4. 常见坑(AI 也常踩)
| 坑 | 纠正 |
|---|---|
建了文件夹却忘记 page.tsx | 404 / 空白 |
在 Server Component 里用 useRouter | 加 'use client' 或改用 Link |
用 next/router(Pages) | App Router 用 next/navigation |
把所有东西塞进根 layout | 按段拆 notes/layout.tsx |
5. 必做验收
- [ ]
/notes、/notes/[id]可访问 - [ ]
notes/layout导航在详情页仍在 - [ ] 有
loading.tsx/error.tsx(可先简单) - [ ] 列表用
Link,不用window.location - [ ] 能画出自己项目的
app/树与对应 URL
6. AI 提示词约束(本篇)
text
技术约束:Next.js App Router(不要 pages/)。
根据我的需求生成文件夹与约定文件列表,
每个文件注明官方文档章节名;不要安装第三方路由库。1
2
3
2
3
7. 下一步
→ 06 · Server Components vs Client Components
官方入口
- https://nextjs.org/docs/app/building-your-application/routing
- https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts
- https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating
- https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
- https://nextjs.org/docs/app/building-your-application/routing/error-handling
