性能
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 个 **有状态的** 值: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 // => 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
)捆绑到您的应用程序中。