性能
SWR 在各种 web 应用中提供了关键功能,因此 性能 是重中之重。
SWR 内置的 缓存 和 重复数据删除 会跳过不必要的网络请求,但 useSWR
hook 本身的性能仍然很重要。在一个复杂的应用中,单个页面渲染可能会调用数百次 useSWR
。
SWR 确保你的应用具有:
- 没有不必要的请求
- 没有不必要的重新渲染
- 没有不必要的代码导入
而无需你更改任何代码。
重复数据删除
在应用中重用 SWR hooks 非常常见。例如,一个应用渲染 5 次当前用户的头像:
function useUser () {
return useSWR('/api/user', fetcher)
}
function Avatar () {
const { data, error } = useUser()
if (error) return <Error />
if (!data) return <Spinner />
return <img src={data.avatar_url} />
}
function App () {
return <>
<Avatar />
<Avatar />
<Avatar />
<Avatar />
<Avatar />
</>
}
每个 <Avatar>
组件内部都有一个 useSWR
hook。由于它们具有相同的 SWR key,并且几乎同时渲染,因此 只会发送 1 个网络请求。
你可以在任何地方重用数据 hooks(比如上面示例中的 useUser
),而不用担心性能或重复请求。
还有一个 dedupingInterval
选项 用于覆盖默认的重复数据删除间隔。
深度比较
SWR 默认 深度比较 数据更改。如果 data
值没有改变,则不会触发重新渲染。
如果你还想更改的话,可以通过 compare
选项 自定义比较函数。比如,某些 API 响应返回一个服务器时间戳,你可能想从数据 diff 中排除它。
依赖收集
useSWR
返回 4 个 有状态的 值:data
、error
、isLoading
和 isValidating
,每个都可以独立更新。例如,如果我们在一个完整的数据请求生命周期中打印这些值,则将如下所示:
function App () {
const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
console.log(data, error, isLoading, isValidating)
return null
}
在最坏的情况下(第一个请求失败,然后重试成功),你将看到 4 行日志:
// console.log(data, error, isLoading, isValidating)
undefined undefined true true // => 开始 fetching
undefined Error false false // => 结束 fetching,出现错误
undefined Error true true // => 开始重试
Data undefined false false // => 重试结束,得到数据
状态的改变是有道理的。但这也意味着组件 渲染了 4 次。
如果我们将组件更改为只使用 data
:
function App () {
const { data } = useSWR('/api', fetcher)
console.log(data)
return null
}
神奇的事情发生了 - 现在只有 2 次重新渲染:
// console.log(data)
undefined // => hydration / 初始渲染
Data // => 重试结束,得到数据
内部发生了完全相同的过程,第一个请求出现了错误,然后我们重试得到了数据。但是,SWR 只更新了组件使用的状态,即:data
。
如果你不是总使用这3种状态,那么你已经从这个特性中获益了。在 Vercel 的实际应用中,这个优化减少了约 60% 的重新渲染。
Tree Shaking
SWR 满足 tree-shakeable 且没有副作用。这意味着如果你只导入核心的 useSWR
API,像 useSWRInfinite
这样的未使用的 API 将不会绑定到你的应用中。