使用 SWR 来管理服务端数据
2022/09/13
背景
先从前端的数据请求开始。
假如有这样一个需求,需要展示一个用户的评论列表,最常见的写法就是在 useEffect
中去获取数据,如下代码所示:
在上面的代码里有三个状态分别是 loadong
,error
和 comments
,你会发现拥有数据请求的组件基本都会类似的状态,于是我们将其封装为一个数据请求的 Hook,这样方便在组件中调用:
现在我们可以直接使用 useRequest
来简化 Comments
中的代码了。
上面是我们非常常见的获取数据的方法,但是让我们考虑以下情况:
- 查看 user1 的评论内容
- user1 请求未响应,数据未渲染到页面中不等待 user1 了,访问 user2 • 浏览器开始请求后台服务器,获取 user2 的内容 • 网络连接没有问题
- user2 请求立即响应了,数据渲染到页面中 user1 的请求响应了
• 通过
setData(json)
覆盖了当前的评论 • 当前应该显示 user2 的评论内容,却显示了 user1
因为网络请求是一个异步的行为,发出请求的顺序与收到响应数据的顺序不一定是一样的,这就是请求的竞态问题。
我们可以用 useEffect
清除副作用来解决这个问题
而上面的useRequest
还有很多问题要处理,比如
请求依赖处理、请求合并去重、请求缓存、请求重试、分页的请求…
正文
与其考虑这么多,为什么不直接使用 SWR 呢,先来看一下 SWR 的特性:
SWR 使用起来非常简单,只需要一行代码即可:
参数:
key
: 可以是字符串、数组或者是null
, 也可以是一个返回上述类型的函数。
fetcher
: 是一个异步函数,它接受 key 并返回数据,是可选的。
options
: 用于配置useSWR
,是可选的。
返回值:
data
: 请求返回的数据,未响应时为undefined
。
error
: 错误信息,没有错误为undefined
。
isValidating
: 是否有请求或重新验证加载。
mutate(data?, options?)
: 更改缓存数据的函数,可以用于数据重新验证。
SWR 可以作为页面中服务端数据的状态管理工具,使用相同的 key
,请求会被自动去重、缓存以及共享。
key
可以根据具体情况来灵活处理:
数据请求的一个例子:
判断有无 error
和有没有 data
是很重要,一般都是先请求出错一次在第二次 render 时才会有数据。
更新数据的一个例子:
使用 swr 的时候:
- 从服务端获取数据:使用
useSWR
实现数据的本地缓存以及验证。
- 数据提交到服务端:编写回调函数的时候使用
mutate
来同步更新本地的缓存。
具体原理分析
key
序列化处理
当 key
为 falsy、空数组时返回空字符串。
- 请求依赖处理
第二个请求依赖第一个请求中的数据,第二个请求一定可以保证在第一个请求结束后再发出。
在处理数据获取的函数 revalidate
中,如果 key
为空字符串, 则不做任何处理,当依赖的接口返回了数据,第二个接口的依赖值 key 会发生改变,会重新触发 revalidate
发起请求。
- 数据重新验证
使用 mutate
函数来完成数据的重新验证比较好理解,只要删除原有的请求标记再调用 revalidate
完成数据的请求即可。