跳到内容
文档
高级
性能

性能

SWR 在各种 Web 应用程序中提供关键功能,因此 **性能** 是重中之重。

SWR 内置的 **缓存** 和 **去重** 跳过不必要的网络请求,但 useSWR 钩子本身的性能仍然很重要。在一个复杂的应用程序中,单个页面渲染可能会有数百个 useSWR 调用。

SWR 确保您的应用程序具有

  • 没有不必要的请求
  • 没有不必要的重新渲染
  • 没有不必要的导入代码

无需您进行任何代码更改。

去重

在您的应用程序中重复使用 SWR 钩子非常常见。例如,一个渲染当前用户头像 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 钩子。由于它们具有相同的 SWR 密钥,并且几乎同时渲染,因此 **只会发出 1 个网络请求**。

您可以随时随地重复使用数据钩子(如上面的示例中的 useUser),而无需担心性能或重复请求。

还有一个 dedupingInterval 选项 用于覆盖默认的去重间隔。

深度比较

SWR 默认情况下会 **深度比较** 数据更改。如果 data 值没有更改,则不会触发重新渲染。

如果要更改行为,还可以通过 compare 选项 自定义比较函数。例如,某些 API 响应返回您可能希望从数据差异中排除的服务器时间戳。

依赖收集

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  // => start fetching
undefined Error false false    // => end fetching, got an error
undefined Error true true      // => start retrying
Data undefined false false     // => end retrying, get the data

状态更改是有意义的。但这同时也意味着我们的组件 **重新渲染了 4 次**。

如果我们将组件更改为仅使用 data

function App () {
  const { data } = useSWR('/api', fetcher)
  console.log(data)
  return null
}

神奇的事情发生了 - 现在只有 **2 次重新渲染**

// console.log(data)
undefined // => hydration / initial render
Data      // => end retrying, get the data

内部发生了完全相同的过程,第一次请求出现错误,然后我们从重试中获得了数据。但是,**SWR 仅更新组件使用的状态**,现在仅为 data

如果您并非始终使用所有这 3 个状态,那么您已经在从这项功能中受益。在 Vercel (在新标签页中打开),这种优化可以减少约 60% 的重新渲染次数。

Tree Shaking

SWR 包是 可树摇的 (在新标签页中打开) 且无副作用。这意味着如果您只导入核心 useSWR API,则不会将未使用的 API(如 useSWRInfinite)捆绑到您的应用程序中。