App Router 与路由基础
2026年9月6日
App Router 是约定式路由:app 里每个文件夹是一段 path,框架按文件名决定这段路由长什么样。不用手写路由表。
文件系统怎么对应 URL
app 下的目录名就是 URL 片段,真正被当成页面的是 page.tsx:
app/
├── page.tsx → /
├── about/page.tsx → /about
├── blog/test/page.tsx → /blog/test
└── contact/page.tsx → /contact
text
只有文件夹、没有 page.tsx,这条路径不会变成可访问页面,但仍可以放 layout、共享组件。
page
page.tsx 是这条路由的叶子。默认是服务端组件,可以直接 async、读文件、打接口。需要点击、输入时,再拆出带 'use client' 的子组件。
layout 和 template
两者都包一层 UI,区别在切页时状态还在不在。
| layout | template | |
|---|---|---|
| 用途 | 导航、侧栏、页脚等共享壳 | 每次进入都要重置的外壳 |
| 切到兄弟路由 | 状态保留 | 重新挂载,状态清掉 |
| 根布局 | app/layout.tsx 必须存在 | 可选 |
多层可以嵌套。同一段同时有 layout 和 template 时,顺序是 layout → template → page。
// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<section>
<h1>Blog</h1>
{children}
</section>
);
}
tsx
如果外壳上有计数器、输入框,用 layout 时在 /blog/a 和 /blog/b 之间跳,数字还在;换成 template.tsx,跳一次就归零。要在外壳里用 useState,文件顶部加 'use client'。
loading
同级放 loading.tsx,这段路由里有未完成的 await 时,Next 会用 React Suspense 先画出它。
// app/blog/loading.tsx
export default function Loading() {
return <p>加载中…</p>;
}
tsx
页面里拖一段时间:
async function getData() {
await new Promise((r) => setTimeout(r, 2000));
return "ok";
}
export default async function Page() {
const data = await getData();
return <p>{data}</p>;
}
tsx
loading.tsx 覆盖的是这一段路由,不是整个站点。更细的粒度可以在页面里自己包 Suspense。
error
error.tsx 是这段路由的 Error Boundary,必须是客户端组件:
"use client";
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<p>{error.message}</p>
<button onClick={() => reset()}>重试</button>
</div>
);
}
tsx
渲染树里抛错会落到最近的 error.tsx。根布局自己炸了要用 global-error.tsx,那是另一层。
not-found
框架自带 404。要改文案,在 app/not-found.tsx(或某段路由下)写自己的页面。主动触发:
import { notFound } from "next/navigation";
export default async function Page() {
const post = await findPost();
if (!post) notFound();
return <article>{post.title}</article>;
}
tsx
一张对照表
| 文件 | 作用 |
|---|---|
page.tsx | 页面本身 |
layout.tsx | 共享壳,切页保状态 |
template.tsx | 类似壳,切页重建 |
loading.tsx | 异步等待时的占位 |
error.tsx | 这段路由的错误边界 |
not-found.tsx | 404 |
下一篇写怎么跳:Link、useRouter、redirect。