Next.js 中不可忽视的路由系统
前言
路由是支撑项目的基石,路由的存在,让页面之间可以实现跳转,传参,甚至是页面的展示都离不开路由。
在 Next.js v13 版本中引入了新的路由模式 App Router,它支持共享布局、嵌套路由、错误处理等,相比于之前的 Pages Router,App Router 制定了更加完善的规范,使代码更好被组织和管理。
定义路由
Next.js 中使用的是基于文件系统的路由,也就是使用文件夹来定义路由,每次定义的文件夹名称就会映射到对应的 URL 中。

要想使 URL 可以访问,必须在其对应的文件夹中存在 page.js
文件,如下图所示,/dashboard/analytics
路由是不可访问的,因为在 analytics
文件夹下不存在 page.js
文件。
其中 page.js
文件是 Next.js 约定的,必须为这个名称,否则不识别,作用是为路由创建用户界面,即 UI。

链接与导航
使用 Link 组件
<Link>
组件是 Next.js 中的一个内置组件,是官方推荐的方式,可以直接在 next/link
中导入并使用。
import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>
}
使用 useRouter 钩子
使用 useRouter
钩子可以让我们以编程式的方式实现导航,直接在 next/navigation
中引入。
值得注意的是,在使用 useRouter
的组件中需要在最顶部书写 use client
用来表示这是一个客户端组件。
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}
使用 redirect 功能
可以在服务端组件使用 redirect
来实现导航,
import { redirect } from 'next/navigation'
async function fetchTeam(id: string) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({ params }: { params: { id: string } }) {
const team = await fetchTeam(params.id)
if (!team) {
redirect('/login')
}
// ...
}
路由组
概念
在 Next.js 的 app
目录中,嵌套文件夹会映射到 URL 路径,Next.js 中有一个路由组的概念,这会帮助我们把文件夹标记为路由组,可以用来防止此文件夹包含在路由的 URL 路径中。
路由组的使用方式:通过将文件夹的名称括在括号中来创建路由组:(folderName)
。
作用
组织路由而不影响 URL 路径
下图中的 (marketing)
和 (shop)
这两个文件夹,这表示创建了两个路由组,在 URL 中将会省略括号中的文件夹,映射到 URL 中的路径就是 /about
和 /blog
。
从而可以使我们在不影响 URL 的情况下组织路由将相关路由保存在一起。

虽然 (marketing)
和 (shop)
这两个路由组中的路由共享相同的 URL 层次结构,我们仍然可以在其文件夹中创建 layout.js
文件,实现不同的路由组能够拥有不同的布局。

选择特定的路由到布局中
例如,我们可以创建一个新的路由组 (shop)
,可以将路由 account
和 cart
放置到能够共享相同布局的路由组 (shop)
中,而路由组之外的 checkout
路由将不会共享路由组中的布局。

创建多个根布局
创建多个根布局,我们可以直接在每个路由组中创建 layout.js
文件,值得注意的是,还需要删除顶层的 layout.js
文件,这有助于我们根据不同的业务需求渲染不同的 UI 组件。
因为顶层的 layout.js
文件被删除,因此还需要将 <html>
和 <body>
标签添加到每个根布局中。

值得注意的是,包含路由组的路由不应解析为与其他路由相同的 URL 路径。
动态路由
概念
我们可以通过将文件夹的名称括在方括号中创建动态段 [folderName]
,如 [id]
、[slug]
等,动态段将会作为 params
属性传递给 layout
、page
等函数中。
示例
例如,博客路由为 app/blog/[slug]/page.js
,其中 [slug]
就可以表示为博客帖子的动态分段。
export default function Page({ params }) {
return <div>My Post: {params.slug}</div>
}
当我们访问 /blog/a
的时候,params
对应的值为 { slug: 'a' }
。
当我们访问 /blog/b
的时候,params
对应的值为 { slug: 'b' }
。
当我们访问 /blog/c
的时候,params
对应的值为 { slug: 'c' }
。
还有一点需要注意,在括号 [...folderName]
内添加省略号,可以扩展动态段以捕获所有后续段。
例如,博客路由为 app/shop/[...slug]/page.js
,相比于 [slug]
这种形式,[...slug]
可以匹配所有的参数。可以匹配 /shop/clothes
、 /shop/clothes/tops
、/shop/clothes/tops/t-shirts
等。
当我们访问 /shop/a
的时候,params
对应的值为 { slug: ['a'] }
。
当我们访问 /shop/a/b
的时候,params
对应的值为 { slug: ['a', 'b'] }
。
当我们访问 /shop/a/b/c
的时候,params
对应的值为 { slug: ['a', 'b', 'c'] }
。
平行路由
平行路由允许我们有条件的渲染同一布局中的一个或者多个页面,例如,使用平行路由可以同时渲染下图中的两个页面,team
和 analytics
页面。

平行路由是使用命令插槽创建的,插槽是按照 @folder
约定定义的,例如,下图中的文件结构中定义了两个插槽 @analytics
和 @team
。

插槽会作为属性传递给共享的父布局,例如上图的文件结构中,app/layout.js
布局组件会接受两个传入的插槽属性 @analytics
和 @team
,与 children
属性并行渲染。
export default function Layout({ children, team, analytics }) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
值得注意的是,平行路由和路由组一样,不会影响 URL。例如,/@analytics/views
文件夹所对应的真实的 URL 地址为 /views
。
而实际上 children
是一个隐式的插槽,意味着 app/page.js
相当于 app/@children/page.js
,因此不需要映射到文件夹。
总结
Next.js 中的路由系统,相比于直接使用 React 开发,从头配置路由便捷很多,同时 Next.js 也帮我们在内部封装了许多功能,就基于文件系统的路由本身来说,大大提高开发者的工作效率,文件即路由,免去那些繁杂的配置,像路由组、平行路由这些出现在 Next.js 中的新概念,更是让我们认识到 Next.js 的强大。
转载自:https://juejin.cn/post/7371641316114464804