Vercel SWR切换路由时数据不更新的正确解决方法
问题根因
这个问题的核心出在SWR的key设计上:
- 你传给
useSWR的第一个参数(请求key)是写死的固定值api/scores,完全没有跟随路由变化。SWR的缓存匹配、请求触发全靠这个key做唯一标识,只要key不变,不管路由怎么切,SWR都会直接返回该key对应的缓存值,哪怕手动调用mutate,请求的还是原来的接口地址,自然拿不到对应分类的比分数据。 next/link触发的是客户端路由跳转,不会整页刷新,也不会重新挂载复用的Scores组件,所以写死的请求地址不会更新。你之前监听router.asPath调用mutate不生效,本质就是触发重验证时,用的还是旧key对应的旧接口,根本没请求当前路由匹配的比分接口。- 用
window.location跳转能正常更新,是因为这种方式会触发整页刷新,整个React应用重新初始化,相当于首次加载页面,自然能拿到对应路径下的初始数据。
正确实现方式
不需要手动监听路由写mutate,只要把SWR的key和当前路由动态绑定即可,SWR检测到key变化时会自动发起新请求、更新数据,同时不同路由的缓存会自动隔离,切回已访问路由时还能直接读缓存实现秒开。
以Next.js App Router为例,代码调整如下:
'use client' import { usePathname } from 'next/navigation' import useSWR from 'swr' import axios from 'axios' // 省略ScoreComponent、fallbackData的引入 export function Scores() { const pathname = usePathname() // 根据当前路由动态生成请求key,不同路由对应不同key const requestUrl = pathname === '/scores' ? '/api/scores' : `/api/scores/${pathname.split('/').pop()}` // 自动拼接/soccer、/basketball后缀 const fetcher = async (url: string) => { const res = await axios.get(url) return res.data } const { data } = useSWR(requestUrl, fetcher, { fallbackData, revalidateOnFocus: true, revalidateOnReconnect: true, refreshInterval: 300000, shouldRetryOnError: false, revalidateOnMount: true, }) if (!data) return <div>比分加载中...</div> return ( <div> {data.map((score) => ( // 记得给列表项加唯一key,避免React渲染异常 <ScoreComponent key={score.matchId} score={score} /> ))} </div> ) }
如果用的是Pages Router,把usePathname换成useRouter()返回的pathname字段即可,核心逻辑完全一致。
额外注意项
- 删掉之前写的监听
router.asPath调用mutate的useEffect逻辑,多余的触发会导致重复请求。 - 接口路径建议统一加上前导斜杠,写成
/api/scores而非api/scores,避免嵌套路由场景下相对路径拼接错误。 - 如果需要在切路由时立刻展示加载态,可以从useSWR返回值里解构
isValidating字段做自定义加载提示。
内容的提问来源于stack exchange,提问作者Jason McFarlane
相关产品推荐
相关产品推荐

