Next.js 是 React 的全栈框架,主打服务端渲染,也就是 SSR(Server Side Rendering)。
它有一套非常强大但也很奇怪的路由机制。
这套路由机制是什么样的?为什么又说很奇怪呢?
我们试一下就知道了。
先创建个 Next.js 项目:
lua
npx create-next-app@latest

执行 create-next-app,输入一些信息,Next.js 项目就创建好了。
进入项目,执行 npm run dev,把它跑起来:

浏览器访问可以看到这个页面,就代表跑成功了:

在项目下可以看到 src 下有个 app 目录,下面有 page.tsx:

我们添加几个目录:

guang/liu/page.tsx
javascript
export default function Liu() {
return <div>666</div>
}
guang/shuai/page.tsx
javascript
export default function Shuai() {
return <div>帅</div>
}
然后浏览器访问下:


可以看到,添加了几个目录,就自动多了几个对应的路由。
这就是 Next.js 的基于文件系统的路由。
刚学了 page.tsx 是定义页面的,那如果多个页面有公共部分呢?
比如这种菜单和导航:

肯定不是每个页面一份。
这种定义在 layout.tsx 里。
app/layout.tsx 是定义最外层的布局:
也就是 html 结构,还有 title、description 等信息:

在网页的 html 源码里可以看到这些:


我们改一下试试:


不止是根路由可以定义 layout,每一级都可以:

javascript
export default function Layout({
children,
}: {
children: React.ReactNode
}) {
return (
<div>
<div>
左侧菜单
</div>
<div>{children}</div>
</div>
)
}
我们给 guang/shuai 这个页面添加了布局,效果是这样的:

Next.js 会自动在 page.tsx 组件的外层包裹 layout.tsx 组件。
有的同学可能会注意到有个渐变背景,这个是 global.css 里定义的,我们把它去掉:


然后继续看:
我们可以使用 Link 组件在不同路由之间导航:


有的同学说,这些都很正常啊。
那接下来看点不那么正常的:
如果我希望定义 /dong/111/xxx/222 (111、222 是路径里的参数)这样的路由的页面呢?
应该如何写?
这样:

javascript
interface Params {
params: {
param1: string;
param2: string;
}
}
export default function Xxx(
{ params }: Params
) {
return <div>
<div>xxx</div>
<div>参数:{JSON.stringify(params)}</div>
</div>
}
路径中的参数的部分使用 [xxx] 的方式命名。
Next 会把路径中的参数取出来传入组件里:

这种叫做动态路由。
那如果我希望 /dong2/a/b/c 和 /dong2/a/d/e 都渲染同一个组件呢?
这样写:

javascript
interface Params {
params: {
dong: string;
}
}
export default function Dong2(
{ params }: Params
) {
return <div>
<div>dong2</div>
<div>参数:{JSON.stringify(params)}</div>
</div>
}
...dong\] 的语法就是用来定义任意层级路由的,叫做 catch-all 的路由。   可以看到,/dong2 下的任意的路由,都会渲染这个组件。 那我直接访问 /dong2 呢?  可以看到,404 了。 但这种也可以支持,再加一个中括号,改成 \[\[...dong\]\] 就好了:  这样 /dong2 也会渲染这个组件,只不过参数是空:  这种 \[\[...dong\]\] 的路由叫做 optional catch-all。 可以看到,Next.js 项目的目录可不只是单纯的目录,都是有对应的路由含义的。 那如果我就是想加个单纯的目录,不包括在路由里呢? 这样写:  我在 dong 和 dong2 的外层添加了一个 (dongdong) 的目录,那之前的路由会变么?   试了下,依然没变。 也就是说只要在目录名外加上个 (),就不计入路由,只是分组用的,这叫做路由组。 现在,我们一个 layout 下渲染了一个 page。 那如果我想一个 layout 渲染多个 page 呢? 这样写:  guang2 下有 3 个 page,page.tsx、@aaa/page.tsx、@bbb/page.tsx 分别会以 children、aaa、bbb 的参数传入 layout.tsx layout.tsx ```javascript export default function Layout({ children, aaa, bbb }: { children: React.ReactNode, aaa: React.ReactNode, bbb: React.ReactNode }) { return (