分为 App Router 和 Pages Router,目前主要用 App Router
路由
路由结构不是通过一般的 router.js 来定义,而是直接通过【文件夹】的组织形式定义的,app 目录下存放所有的路由文件。
文件夹下面有 page.tsx 文件,路由才可公开访问。而且,即使一个路由被设置为公开访问,也只会有 page.tsx 返回的内容发送给客户端。
| 文件路径 | 对应的 URL | Notes |
|---|---|---|
app/page.tsx | / | 根路由 |
app/blog/page.tsx | /blog | 页面路由 |
app/blog/authors/page.tsx | /blog/authors | 页面路由 |
动态路由
使用方括号 [] 作为文件名:使用 [segment] 表示单个参数, [...segment] 表示捕获所有, [[...segment]] 表示可选捕获所有。
| 文件路径 | 可能对应的 URL |
|---|---|
app/blog/[slug]/page.tsx | /blog/post |
| --- | |
app/blog/[...slug]/page.tsx | /blog/post, /blog/my-blog/post |
| --- | |
app/blog/[[...slug]]/page.tsx | /blog, /blog/post, /blog/my-blog/post |
分组
使用小括号 () 作为文件名:不会出现在 URL 里,如 app/(my-blog)/blog/page.tsx 对应的还是 /blog。适合用来组织一批类似的路由文件夹
私有文件夹
在文件夹名前加上下划线:_folderName,表示该文件夹是一个私有实现细节,将此文件夹及其所有子文件夹排除在路由之外
路由跳转
1.使用内置组件
使用 <Link> 组件在路由之间进行导航:
import Link from 'next/link'
export default async function Post({ post }) {
const posts = await getPosts()
return (
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}2.使用 useRouter
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}router.push(href: string, { scroll: boolean }):进行路由跳转,向浏览器的历史记录堆栈添加一个新记录router.replace(href: string, { scroll: boolean }):进行路由跳转,但不向浏览器的历史记录堆栈添加新记录router.refresh(): 刷新当前路由router.prefetch(href: string, options?: { onInvalidate?: () => void }): 预取提供的路由以实现更快的客户端过渡router.back(): 在浏览器的历史记录栈中导航回上一个路由router.forward(): 在浏览器的历史记录栈中导航到下一个页面
默认情况下,当导航到新路由时,Next.js 会自动滚动到页面顶部。使用 scroll: false 阻止滚动
获取路由信息
useParams
当前 URL 动态路由中的参数
import { useParams } from 'next/navigation'
export default function ExampleClientComponent() {
const params = useParams<{ tag: string; item: string }>()
// Route -> /shop/[tag]/[item]
// URL -> /shop/shoes/nike-air-max-97
// `params` -> { tag: 'shoes', item: 'nike-air-max-97' }
console.log(params)
return '...'
}| Route | URL | useParams() |
|---|---|---|
app/shop/page.js | /shop | {} |
app/shop/[slug]/page.js | /shop/1 | { slug: '1' } |
app/shop/[tag]/[item]/page.js | /shop/1/2 | { tag: '1', item: '2' } |
app/shop/[...slug]/page.js | /shop/1/2 | { slug: ['1', '2'] } |
usePathname
读取当前 URL 的路径名
import { usePathname } from 'next/navigation'
export default function ExampleClientComponent() {
const pathname = usePathname()
return <p>Current pathname: {pathname}</p>
}| URL | 返回值 |
|---|---|
/ | '/' |
/dashboard | '/dashboard' |
/dashboard?v=2 | '/dashboard' |
/blog/hello-world | '/blog/hello-world' |
useSearchParams
读取当前 URL 的查询字符串
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// URL -> `/dashboard?search=my-project`
// `search` -> 'my-project'
return <>Search: {search}</>
}useSearchParams 返回 URLSearchParams 接口的只读版本,有着共同的实用方法