Skip to content
文档
高级
性能

性能

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 个 有状态的 值:dataerrorisLoadingisValidating,每个都可以独立更新。例如,如果我们在一个完整的数据请求生命周期中打印这些值,则将如下所示:

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 (opens in a new tab) 的实际应用中,这个优化减少了约 60% 的重新渲染。

Tree Shaking

SWR 满足 tree-shakeable (opens in a new tab) 且没有副作用。这意味着如果你只导入核心的 useSWR API,像 useSWRInfinite 这样的未使用的 API 将不会绑定到你的应用中。