React 优化:在 ahooks - useRequest 中利用 swr 优化网络请求

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情

这是我关于 ahooks - useRequest 系列文章的第三篇,前两篇请查看:

通过前两篇文章我们已经基本了解了关于服务端状态管理的概念,也通过 useRequest 体验了一系列有趣的功能,认识到了其强大之处。

本文我们将继续介绍 useRequest 的进阶用法,主要是:swr 。

什么是 swr

什么是 swr?我们这里引用一段 SWR 官方的介绍:

The name “SWR” is derived from stale-while-revalidate, a HTTP cache invalidation strategy popularized by HTTP RFC 5861. SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.

“SWR”这个名称来源于 stale-while-revalidate,这是一种由 HTTP RFC 5861 推广的 HTTP 缓存失效策略。SWR 是一种首先从缓存中返回数据(stale),然后发送 fetch 请求(revalidate)的策略,最终使用新的数据。

简而言之,当我们的某个请求存在缓存时,我们优先使用缓存数据展示在页面上,同时在背后发出请求,请求成功后再使用最新获取的数据来更新UI。

这种方式可以提升用户体验,使得 UI 界面在用户眼中总是有内容有数据的。在大部分场景中,当频繁的页面打开、回退时,用户并不在意其中的数据实时性。

很多数据甚至本就不具备实时性,这样的数据如果可以缓存,无疑可以减少 loading 时长。

使用 swr

在 useRequest 中也是具备了 swr 的能力的,使用起来也是十分简单,最简单的方式就是配置一个 options.cacheKey,是的,你只需要配置这个 cacheKey 就可以让你的请求具备 swr 的能力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 请求知乎专栏的接口
const getZhuhuColum = async (id) => {
const url = `/api/columns/${id}`;
const res = await fetch(url);
let result = await res.json();
result = {
title: result.title,
// 通过时间戳我们可以看出数据是否为缓存
time: Date.parse(new Date())
}
return result;
}

export const Zhihu = () => {
const { data, loading } = useRequest(getZhuhuColum, {
defaultParams: ['feweekly'],
// 配置cacheKey
cacheKey: 'columns/feweekly'
});
return (
<div>
<span>isLoading: {String(loading)}</span>
<br />
{data && <span>{JSON.stringify(data)}</span>}
</div>
)
}

现在我们的接口已经自动的具备了缓存,每次发起请求时,会优先从缓存中获取数据用于显示,当接口返回后,才会使用新的数据更新 UI 与缓存。

Gif效果对比:

未使用 swr,每次都要等待重新请求后才能填充数据:

未使用cacheKey.gif

使用 swr,先使用缓存数据填充,等到新数据请求完成后再更新:

使用cacheKey.gif

可以清楚的看出,未使用时,每次请求都是先 loading 然后显示内容。

而使用 swr 后,第一次也是先loading 然后显示内容,再之后的每次都是先使用缓存,再更新UI。

配置缓存时间与新鲜时间

options.cacheTime 可以配置缓存时间,超时后会移除缓存,单位为毫秒,默认值是 300000 (5分钟)。该参数设置主要针对的是,两次请求之间缓存有效期,例如我们设置 options.cacheTime 为 1000,但是我们两次触发请求的间隔为 2 秒,那么表现出来的效果等同于没有配置缓存。

options.staleTime 可以配置数据保鲜时间,单位为毫秒,默认值 0 。在保鲜期内的我们认为数据是可靠的,即使请求被重新触发,也不会在背后发起真实的请求,直到过了数据保鲜期才会真正的发送请求。

1
2
3
4
5
const { data, loading } = useRequest(getZhuhuColum, {
defaultParams: ['feweekly'],
cacheKey: 'columns/feweekly',
staleTime: 5000
});

这里我录制了一个Gif ,可以很好的展示这一点:

设置数据保鲜期.gif

我们可以看到在第一条数据发送后,尽管我们多次重新挂载组件触发请求,但是都没有真正发起请求,使用的都是缓存中的数据,直达过了数据保鲜期后,才发出了第二条请求。

数据共享

我们使用 swr 时还有一点需要注意,那就是数据共享,同一个 cacheKey 的数据时全局共享的,这使得:

  • 请求 Promis 共享,多个具有相同 cacheKey 的请求,同时只会有一个发起请求,后发起的将会共用该Promise
    image.png
    从这张图片也能看出,两个组件同时发起了相同的请求,实际只发出了一次真正的请求。

  • 当这些请求中的某一个重新发起更新了数据后,其他的也会一起变更,就如同我们使用 redux 进行全局状态管理的效果一样。

    注意这里无论是 mutate 、run、还是 refresh ,只要任意某个请求触发了数据状态的变化,那么全局内数据状态都会变化。

基于上面两点,我们一定要注意 cacheKey 的设置,一般的我们以 接口名称+参数 的拼接字符串作为 key,如果你用过 react-query,就会发现这和 RQ 中的 QueryKey 是一个概念(但是这里类型只能是字符串,RQ支持的类型更多)。

删除缓存

既然由缓存,那自然也就有缓存的移除,ahooks 提供了一个函数,专门用于移除指定 cacheKey 的缓存数据

1
2
3
4
import { useRequest, clearCache } from 'ahooks';

// 需要移除时,传入指定的cacheKey即可,非常方便
clearCache('cacheKey');

什么情况我们需要使用这个函数?

举个例子,我们访问了某个列表页面,请求了一次列表(使用了cacheKey缓存),然后我们在其中的一项的详情页面,删除了这一项。

这时如果此时还处于我们设置的数据保鲜期options.staleTime内,再回到列表页面时,是不会触发请求的,会直接使用就的缓存(此时已经减少了一项),这样显然是很差的体验。

这时我们就可以在删除事件中调用删除缓存函数,这样再回到列表页面时,由于没有缓存数据,会再次发起请求,使用新的数据!

clearCache 函数还可以同时删除多个,只需要传入 cacheKey 的数组即可

总结

通过三篇文章的介绍,想必大家对服务端状态管理这个概念已经有了更为深刻的认识了吧,如果你觉得本文有帮助到你,欢迎点赞、收藏,更多有关 React 文章,请关注我的专栏。