缓存
缓存涉及到的东西可能有点多,所以这部分先写一点。官网一个例子可以参考下
1. 缓存做了什么
简单的来说,就是把每次请求都保存在了内存,下次请求前会先取缓存,再对比响应更新数据。
做个简单的示例:
- 子组件和父组件都会请求同一个
api地址,初始情况下父组件请求标题1-1 ~ 标题1-5,子组件请求标题2-1 ~ 2-5 - 标题1和标题2通过传的参数来区分,也就是图片上的参数a和参数b
// 父组件querys = {} 子组件querys={a: "1", b: "1"}
const posts = useQuery({
queryKey: ['posts', querys],
queryFn: () => getPosts(querys),
})
1.1 初始渲染时
父组件没有参数,所以请求回来了标题1-x
子组件默认携带参数{a: 1, b: 1},所以请求回来了标题2-x
1.2 父组件传递和子组件相同的参数
在输入框里输入1,点击提交,父组件此时传递参数{a: 1, b: 1}。
这里注意:
- 请求并没有结束
fetchStatus仍然是fetching,但是数据已经变成了新的,这是因为直接取了缓存 - 因为参数不一样,所以仍然会重新请求
2. staleTime过期时间
这是很常见的一个配置选项,在query中默认是0代表请求完了就过期。
当你设置了refetchOnMount, refetchOnWindowFocus, refetchOnReconnect 和refetchInterval时,如果数据已经过期,就会重新请求。
- 生成
query实例初始化时,一般是从inactive状态恢复到active时,如果你配置了refetchOnMount: false,那么也不会自动请求 - 重新聚焦网页时
- 网络发生变化时
3. cacheTime请求对象回收清理时间
默认是5分钟,很容易和staleTime搞混,在网上也有很多人讨论,我看了个大概说说我的理解。
想象一下这种情况:
- 项目开启了
refetchOnWindowFocus,这样过期(stale)的数据都可以在重新聚焦的时候重新请求,更新。 - 现在有两个模块,模块A请求
api-1、api-1-1、api-1-2、api-1-3....等,模块2请求api-2...等请求。 - 用户长时间停留在模块2操作,所以模块的1的请求肯定是过期了,如果没有设置
cacheTime,那么当用户重新聚焦页面时,除了模块2的数据重新请求了,模块1的一堆请求也会被发出去,显然没有必要
所以cacheTime的作用简单来说就是,当组件被卸载、切换了路由或者其他情况导致不能监听这个请求对象的情况下,过多长时间回收掉(想象成垃圾回收机制)。
举个例子:
- 左边开发面版可以看到有几个请求实例,当前页面在
react-query - 现在我切换到
测试页面,发现所有的请求实例都变成了inactive。 - 我设置了
cacheTime为5 * 1000也就是五秒,所以五秒之后会发现不活跃的请求都被清理掉了。
4. Query Invalidation手动过期数据
我发现使用react-query需要摒弃以前的思想,以前使用其他库的时候动不动就refetch,而在这里面,只有过期的数据和不一样的数据才需要重新获取,所以提供了invalidateQueries。
具体api就不赘述了,主要讲讲和refetch的区别。这里有个讨论
invalidateQueries只是标记,而不是直接重写请求invalidateQueries有更细腻灵活的配置,可以精确到某些请求
// 比如这个invalidateQueries,精确到参数version>10的请求
queryClient.invalidateQueries({
predicate: (query) =>
query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10,
})
// 这个请求会被设置为过期
const todoListQuery = useQuery({
queryKey: ['todos', { version: 20 }],
queryFn: fetchTodoList,
})
// 这个请求会被设置为过期
const todoListQuery = useQuery({
queryKey: ['todos', { version: 10 }],
queryFn: fetchTodoList,
})
// 这个请求不会
const todoListQuery = useQuery({
queryKey: ['todos', { version: 5 }],
queryFn: fetchTodoList,
})