Next 路由篇
路由导航
Link
基本使用
1 |
|
useRouter
1 |
|
使用 useRouter 必须在客户端组件,也就是在文件顶部加上 'use client'
还有就是 useRouter
是 “next/navigation” 导入的,不是 “next/router”。
redirect
1 |
|
服务端组件使用 redirect 进行重定向跳转。
history
history 是浏览器提供的原生跳转方式,有 pushState 和 replaceState 两种方式,前者是向路由栈推一条新的路由,后者是直接将当前条替换掉
跳转行为
在 App Router 下跳转后默认滚动到顶部,使用导航的前进后退会保持原来的滚动距离
1 |
|
通过给 Link 标签添加 scroll 属性,或者使用 router 跳转时第二个参数对象加上 scroll: false
禁用行为。
动态路由
动态路由需要用[]
将文件夹名包住,例如新建如下结构目录:
在 src/app/dynamic-routing/[id]/page.js
就是一个 id 的动态路由
1 |
|
这时访问 /dynamic-routing/111
页面现实的结果就是 111
但如果有多个参数的情况以上方法就不好用了,这时只需要将文件夹的名改成 [...params]
,声明接收所有参数
1 |
|
这时我们访问 /dynamic-routing/name/age/hobby
来看下效果
可以看到的是参数是以数组的形式传递进来的。
路由组
通过使用 ()
包裹文件夹名创建分组,通过 ()
创建的文件夹不会出现在路由的路径中,例如下图 (group1)
和 (group2)
并不会对路由产生影响,也正因如此,即使在不同分组下,也不能创建同名的文件夹
多个根布局
使用路由组后,可以给每个组单独设置 layout,也就是在每个分组下新建 layout.js,如果这样做的话,就需要删除掉 app 下的 layout.js,并且因为是根布局,每个分组下的 layout 都需要有 和
标签。只是删除 app 下的 layout.js 的话,还是会报错,因为还需要将 app 下的 page.js 放入到某一个分组下,需要注意的是,放在某个分组下就会使用这个分组的 layout。
平行路由
当一个页面布局需要多个页面显示在一起的时候可以使用平行路由,通过 @
作为文件夹的开头声明这是一个平行路由,在平行路由的 layout 中通过 props 拿到并渲染
layout.js
1 |
|
并且平行路由还可以嵌套子路由,例如在 @address 下新增 detail/page.js ,然后在 layout 中加入跳转 <Link href="/detail">to detail</Link>
可以看到当进行跳转时,只有 address 部分进行了更新,然后我们刷新页面会发现页面404了
为什么会出现以上的情况呢?简单来说我们给 @address 加了 detail 的子路由,而 @contact 没有,当通过 Link 软导航跳转时,Next 进行了部分渲染,也就是对匹配上路由的 @address 进行更新,而 @contact 匹配不上,保留了原来的状态,当我们刷新浏览器进行硬导航时,Next 没办法判断不匹配路由,也就是 @contact 的状态没办法进行渲染,所以 404。
想要解决 404 的问题,我们需要给不匹配的路由增加 default.js 在不匹配时显示
效果
路由拦截
Next 提供路由拦截功能,当从一个路由跳转到新的路由可以进行拦截渲染。
实现方式:在文件夹名前加上 (.)
声明拦截路由,按照官网说的不同数量的 . 代表拦截不同层级
- (.)来匹配同一层级
- (..)来匹配上一层级
- (..)(..)来匹配上两层
- (…)来匹配应用程序根目录
我们新建一个 product ,这里希望使用 (.)detail 拦截 '/product/detail'
1 |
|
剩下的文件随便写点内容即可,我们看下效果
可以看到当我们从 /product 跳转到 /product/detail 时被拦截了,而我们硬导航刷新页面则不会被拦截。
路由处理程序
Next 中命名为 route.js 的文件路由处理程序,也就是负责请求的文件,该文件必须在 app 目录下,并且不能和 page.js 同级。因为在同级的情况下,请求和页面路径相同产生冲突。
我们新增 api/test/route.js 文件
1 |
|
这里写了一个最简单的 GET 请求,通过 /api/test
即可访问接口,除了 GET 请求还有 POST、PUT、PATCH、DELETE、HEAD 和 OPTIONS ,另外每个请求都有两个参数 request 和 context。
1 |
|
request
request 是对 Web Request API 的扩展
通过 request 获取 search 参数
1 |
|
通过 request.nextUrl.searchParams.get() 的方法来获取 search 参数
通过 request 获取 body 参数
1 |
|
这里我们写一个 POST 请求,然后随便传递参数进来看打印结果。
context
context 只有一个 params,这里我们新建目录来测试
我们让接口地址传入一个 name 参数进来,看看 context 会打印出什么
我接口调用 /api/jack
可以看到 context 输出了 name为 jack 的对象,如果有多个参数需要传递,也可以使用上文说到的 [...params]
的方式命名文件夹,参数会以数组的形式传递进来。
缓存
NextResponse
NextResponse 是 Next 提供的网络响应 API,他是对Response的扩展,在生产环境下,使用 Response 或者 NextResponse 的 GET 请求会被缓存。
1 |
|
我们写一行这样一段 api 然后 执行 npm run build
打包,再执行 npm run start
构建
这时我们访问这个接口发现日期是不会更新的
退出缓存的方式
- GET 请求使用 Request 对象
1
2
3
4export async function GET(request) {
const params = request.nextUrl.searchParams
return NextResponse.json({ data: new Date().toLocaleTimeString(), params: params.toString() })
} - 添加其他的请求,如 POST
- 手动声明为动态模式
1
2
3
4
5
6
7import { NextResponse } from 'next/server'
export const dynamic = 'force-dynamic'
export async function GET() {
return NextResponse.json({ data: new Date().toLocaleTimeString() })
} - 设置缓存失效
1
2
3
4
5
6// 设置重新验证频率为 10s 就是十秒后第一次请求还是会拿到缓存值,再次请求才会拿到新的值
export const revalidate = 10
export async function GET() {
return NextResponse.json({ data: new Date().toLocaleTimeString() })
}
路由中间件
中间件一般用来拦截应用的请求和响应,一般写在 src 文件夹下,也就是存放路由页面的根目录(当然可以不是 src)。
官网给了一个中间件的例子,功能就是拦截一个路由并且进行重定向。这里可以根据自己的例子修改下路由即可。
1 |
|
效果图
可以看到当访问 /product/detail 时,被重定向到 / 说明我们写的 middleware 生效了。
matcher
matcher 就是用来选中拦截的路由,只有一个时用字符串即可,就像上面的例子一样,如果是多个情况,也可以使用数组。还可以用()
包裹正则表达式进行匹配。
官网提供了一个例子,匹配了除特定路径之外的所有路径。
1 |
|
matcher 还可以是一个对象数组,通过对象属性进一步严格匹配,has 匹配有该属性和值,missing 匹配不存在该属性和值。
1 |
|
就像上面例子,missing 规定 header
不能存在 next-router-prefetch
且 purpose
值不为 prefetch
,has 规定 header
的 Authorization
的值需要为 Bearer Token
的。
middleware
匹配路由除了使用 config 的 matcher 还可以直接在 middleware 中进行判断,效果和上面是一样的。
1 |
|
读取和设置 cookie
1 |
|
通过 request 的 get 和 getAll 方法读取 cookie 的值,通过 has 查看是否存在某值,通过 delete 方法删除 cookie 的值。
NextResponse.next()
执行该方法会返回一个新的 Response 对象,通过 set 方法设置 cookie 的值。
读取和设置 header
1 |
|
响应头加上了 x-hello-from-middleware2
cors
1 |
|
生成响应
通过 Response 或 NextResponse 直接返回响应
1 |
|
高级中间件标志
skipMiddlewareUrlNormalize
跳过尾部斜杠重定向
1 |
|
1 |
|
以上代码实现除了 /product 为前缀的路由外,其他路由加上尾斜杠
skipMiddlewareUrlNormalize
开启时在 middleware 中获取的 req.nextUrl.pathname 为原始的地址。
Runtime
middleware(截至目前 Next 最新版本为 14.2.5) 只支持 Edge runtime,不支持 node runtime,所以写 middleware 时尽量使用 web API 不要使用 node API。
路由段配置
路由段指的是斜杠分割的路径,比如 /api/user 中 /
是根段,api
是段 user
是叶段。
配置项
1 |
|
dynamic
更改布局或者页面的动态行为
可选值:'auto'(默认) | 'force-dynamic' | 'error' | 'force-static'
- auto: 自动判断
- force-dynamic: 强制使用动态渲染,退出所有 fetch 请求缓存
- error:强制静态渲染并且缓存数据,如果有组件使用动态函数或者不缓存数据请求,会报错
- force-static:强制静态渲染并且缓存数据,同时 cookie() headers() useSearchParams() 返回空值。
dynamicParams
可选值:true(默认) | false
- true: 按需生成
- false: 返回 404
revalidate
设置重验证的时间间隔,单位为秒。此设置不会覆盖单个 fetch 请求设置的 revalidate 的值。
可选值:false | ‘force-cache’ | 0 | number
- false: 不重新验证
- 0:总是动态渲染
- number:重新验证的时间间隔
fetchCache
可选值:'auto' | 'default-cache' | 'only-cache' | 'force-cache' | 'force-no-store' | 'default-no-store' | 'only-no-store'
- auto: 自动判断
- default-cache: 默认缓存
- only-cache:如果开发者未设置 cache 选项,默认设置为 force-cache,如果有请求设置成 cache: ‘no-store’,则会导致报错
- force-cache:将所有请求的 cache 选项设置为 force-cache 。
- default-no-store:开发者可以自由设置 cache 选项,但如果开发者未设置 cache 选项,默认设置为 no-store,这意味着即使是在动态函数之前的请求,也会被视为动态。
- only-no-store:如果开发者未设置 cache 选项,默认设置为 no-store,如果有请求设置成 cache: ‘force-cache’,则会导致报错
- force-no-store:将所有请求的 cache 选项设置为 no-store 。
runtime
运行时环境设置
可选值:'edge' | 'nodejs'