Appearance
路由
旧款路由 Page Router ,新款路由 App Router 。
Page Router ,about.ts contact.ts 映射为 /about 和 /contact ,意味着组件 table.ts list.ts 得另外存放。两个文件夹,页面和组件,维护麻烦。
App Router ,考虑页面和组件共存,加深文件层级,约定页面为 page.ts ,布局为 layout.ts ,加载为 loading.ts 。about/page.ts contact/page.ts 映射为 /about 和 /contact 。
加深层级,依赖约定,利弊各半。
加载边界,简单方案使用约定 loading.ts 。
// app/book/loading.ts
export default function Loading() {
return <div>Loading...</div>;
}缩小加载边界,可以使用 React 组件 <Suspense> 。
新建文件 app/book/data.json ,模拟数据。
[
{
"id": 1,
"name": "西游记"
},
{
"id": 2,
"name": "水浒传"
},
{
"id": 3,
"name": "三国演义"
},
{
"id": 4,
"name": "红楼梦"
}
]动态路由,服务端异步
// app/book/[slug]/page.tsx
// 详情页,对应 URL 为 /book/[slug]
export default async function Page({
params
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params;
return <div>图书 {slug}</div>;
}查询字符串,search page pageSize 等,服务端异步
// app/book/page.tsx
// 列表页,对应 URL 为 /book
export default async function Page({
searchParams
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const { search = '', page = '1', pageSize = '10' } = await searchParams;
return <div>
搜索 {search},第 {page} 页,每页 {pageSize} 条
</div>;
}查询字符串,客户端
// app/book/search.tsx
// 组件
'use client'
import { useSearchParams } from 'next/navigation';
export default function SearchBar() {
const searchParams = useSearchParams();
const search = searchParams.get('search') ?? '';
return <div>搜索 {search}</div>;
}