跳转到内容

基于文件的路由

Pareto 使用基于文件的路由。app/ 目录下的每个 page.tsx 都会成为一个路由。

文件用途
page.tsx路由组件
layout.tsx包裹布局(从根到页面嵌套)
loader.ts独立的 loader 文件,用于服务端数据获取
head.tsx路由级 <title> 和 meta 标签
not-found.tsx404 页面(仅限根级)
error.tsx错误页面 — 捕获 loader 和渲染错误(仅限根级)
document.tsx文档定制getDocumentProps() 设置 <html> 属性(仅限根级)
route.ts资源路由(JSON API,无 HTML)
app/
page.tsx → /
stream/
page.tsx → /stream
blog/
[slug]/
page.tsx → /blog/:slug
api/
users/
route.ts → /api/users (JSON)

使用 [param] 目录名来定义动态段:

app/blog/[slug]/page.tsx → /blog/:slug

通过 loader 上下文访问参数:

export function loader(ctx: LoaderContext) {
const { slug } = ctx.params
return { post: getPost(slug) }
}

使用 [...param] 实现通配段:

app/docs/[...path]/page.tsx → /docs/*

path 参数是一个字符串,包含 URL 的剩余部分。例如 /docs/getting-started/install 会将 ctx.params.path 设为 getting-started/install

使用 [[...param]](双括号)实现可选通配段。与通配路由类似,但也会匹配父路径:

app/docs/[[...path]]/page.tsx → /docs 和 /docs/*

这会同时匹配 /docsctx.params.path 为 undefined)和 /docs/getting-startedctx.params.pathgetting-started)。

每一级的布局会包裹其子组件。根级 layout.tsx 包裹所有页面:

app/
layout.tsx ← 包裹所有内容
page.tsx ← / (被根布局包裹)
dashboard/
layout.tsx ← 包裹 dashboard 页面
page.tsx ← /dashboard (被两层布局包裹)
settings/
page.tsx ← /dashboard/settings (被两层布局包裹)

可以在单独的文件中定义 loader 吗?

Section titled “可以在单独的文件中定义 loader 吗?”

你可以在独立的 loader.ts 文件中定义路由的 loader,而不是从 page.tsx 中导出:

app/dashboard/loader.ts
import type { LoaderContext } from '@paretojs/core'
export function loader(ctx: LoaderContext) {
return { stats: getDashboardStats() }
}

这样可以将数据获取逻辑与组件分离。如果同时存在 loader.tspage.tsx 中的 loader 导出,loader.ts 优先。

如果你想在路由之间共享布局但不添加 URL 段,使用括号包裹的目录名:

app/
(marketing)/
layout.tsx ← 营销页面的共享布局
page.tsx ← / (没有 /marketing 前缀)
about/
page.tsx ← /about
(dashboard)/
layout.tsx ← 仪表盘页面的共享布局
overview/
page.tsx ← /overview

每个路由可以通过 head.tsx 文件定义自己的 <title> 和 meta 标签。详见 Head 管理

在布局或页面中使用 ParetoErrorBoundary 捕获渲染错误。你可以在任意层级放置错误边界,实现细粒度的错误隔离。