SWR
- 安装:
yarn add swr
1. 语法介绍
next.js
团队创建了一个名为swr
的react hook
,用于数据获取。如果您在客户端获取数据,我们强烈建议您使用这个hook
。它具有缓存,自动重新请求,窗口焦点跟踪,客户端周期性请求数据的功能。
-
api用法
const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
useSWR
参数说明key
: 请求的唯一key
,类型可以是string
、function
、array
、null
。fetcher
:(可选)一个请求数据的Promise
返回函数。options
:(可选)该SWR hook
的配置对象。
useSWR
返回值说明data
: 通过fetcher
用给定的 key 获取的数据。error
:fetcher
抛出的错误。isLoading
: 是否有一个正在进行中的请求且当前没有 “已加载的数据”。预设数据及之前的数据不会被视为 “已加载的数据”。mutate(data?, options?)
: 更改缓存数据的函数。isValidating
: 是否有请求或重新验证加载。
-
isLoading 和 isValidating 的区别
- 在 SWR 中,虽然可以使用 !error && !data 来作为 isLoading 的状态,但 useSWR 本身也会返回 isValidating。这两个返回值看起来好像很像,但实际上 isValidating 是只要有 API 请求被发送的话,isValidating 都会从 false 变成 true,请求结束后才再变回 false;而 isLoading 则不太一样,它只有在第一次向 API 发送请求时,data 和 error 都是空的时候才会是 true,一旦这个 API 在未来被再次请求时,它的状态就一直是 false,不再是 true。
- 因为 swr 具有缓存特性,它首先将第一次接口请求的数据缓存起来,然后再次去请求同一个接口数据的时候,会将缓存值和最新值做对比,如果缓存值与最新值相同,则不用更新缓存;否则用最新值来更新缓存数据,同时更新UI展示效果。
2. 数据请求
你可以使用任何请求库来处理数据请求,比如:fetch、axios、graphql等。
- 新建
pages/api/user.js
接口文件,并新增内容如下// 为了使 API 路由能正常工作,你需要导出(export)一个默认函数 handler(即 请求处理器),并且该函数能够接收以下参数: req、res export default function handler(req, res) { return res .status(200) .json([ { name: "John Doe1" }, { name: "John Doe2" }, { name: "John Doe3" }, ]); }
- 新建
pages/ajax/index.jsx
路由组件,测试swr
请求export default function Ajax() { return <div className="Ajax"></div>; }
2.1 fetch请求
import useSWR from "swr";
const fetcher = (url) => fetch(url).then((r) => r.json());
export default function Ajax() {
const { data, error, isLoading } = useSWR("/api/user", fetcher, {fallbackData: []}); // fallbackData: data变量的初始数据
if (isLoading) return <div className="Loading">loading...</div>;
if (error) return <div className="Error">{error.message}</div>;
return (
<div className="Ajax">
{data.map((item) => (
<h5 key={item.name}>{item.name}</h5>
))}
</div>
);
}
2.2 axios请求
import useSWR from "swr";
import axios from "axios";
+ const fetcher = (url) => axios.get(url).then((r) => r.data);
- const fetcher = (url) => fetch(url).then((r) => r.json());
export default function Ajax() {
const { data, error, isLoading } = useSWR("/api/user", fetcher, {fallbackData: []}); // fallbackData: data变量的初始数据
if (isLoading) return <div className="Loading">loading...</div>;
if (error) return <div className="Error">{error.message}</div>;
return (
<div className="Ajax">
{data.map((item) => (
<h5 key={item.name}>{item.name}</h5>
))}
</div>
);
}
3. 全局配置
SWRConfig
组件可以为所有的SWR hook
提供全局配置,被该组件包裹的所有组件的内容,都会默认加载这些全局配置。
3.1 将 fetcher 函数配置在全局
如果多个组件中都要使用
swr
发送请求,那每个组件文件中都要导入swr
,并设置一个fetcher
请求函数,比较繁琐,因此,可以将其配置在App
根组件的外部,这样所有的内部子组件都可以使用这个fetcher
发送请求了。
- 修改
pages/_app.jsx
根组件内容import { SWRConfig } from "swr"; function App({ Component, pageProps }) { return <SWRConfig value={{ fetcher: (resource, init) => fetch(resource, init).then((res) => res.json()), }} > {getLayout(<Component {...pageProps} title="额外的数据" />)} </SWRConfig> }
- 各个组件在使用
swr
时,默认使用全局配置import useSWR from "swr"; export default function Ajax() { const { data, error, isLoading } = useSWR("/api/user"); // 如果 fetcher 是全局配置 `<SWRConfig>` 提供的,第二个参数可以忽略。 return ( <div className="Ajax"> ... </div> ); }
3.2 全局错误拦截
你总是可以响应性的在组件内部得到 error 对象。但如果你想要全局处理错误,通知 UI 显示一个 toast 或者一个 snackbar,或在某处收集它,可以用 onError 事件:
<SWRConfig value={{
onError: (error, key) => {
if (error.status !== 403 && error.status !== 404) {
// 显示一个通知 UI。
}
}
}}>
<MyApp />
</SWRConfig>
4. 自动重新请求
4.1 聚焦时重新请求
当你重新聚焦一个页面或在标签页之间切换时,SWR 会自动重新请求数据。这个功能非常实用,可以保持网站同步到最新数据。对于在长时间位于后台的标签页,或 休眠 的电脑等情况下刷新数据也很有帮助。该特性默认是启用的。你可以通过 revalidateOnFocus 选项禁用它。
const { data, error, isLoading } = useSWR("/api/user", {
revalidateOnFocus: false, // 窗口聚焦时自动重新请求
});
4.2 定期轮询请求
SWR 会为你提供定时重新请求数据的选项。以达到轮询的目的。
const { data } = useSWR("/api/user", {
refreshInterval: 1000,
});
- 默认 disabled: refreshInterval = 0,不开启轮询。
- 如果设置为数字,轮询间隔(以毫秒为单位)。
- 如果设置为函数,该函数将接收最新数据,并且应以毫秒为单位返回间隔。
4.3 网络重连自动请求
在用户解锁了他们的计算机但网络还没有连上时。为了确保页面数据始终是最新的,SWR 会在网络恢复时自动重新请求。
-
该特性默认是启用true。你可以通过
revalidateOnReconnect
选项禁用它。const { data } = useSWR("/api/user", { revalidateOnReconnect: false, });
4.4 禁用自动重连
如果数据是不变的,即使我们重新请求也永远不会发生任何改变,那么我们可以禁用它的所有的自动重新请求,直接将请求结果缓存即可。
- SWR 提供了一个辅助 hook
useSWRImmutable
来标记数据为不可变数据。import useSWRImmutable from 'swr/immutable' useSWRImmutable(key, fetcher, options)
- 一旦数据被缓存,在切换路由时,swr 将永远不会再次请求。除非刷新页面才会产生新的请求。
const { data } = useSWR(key, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false }) // 相当于 const { data } = useSWRImmutable(key, fetcher)
5. 请求参数
在某些场景中,可能向 fetcher 函数传递多个参数(可以是任何值或对象),你可以使用一个数组作为参数 key,它包含 fetcher 的多个参数。
- 默认情况下,key 将作为参数传递给 fetcher。以下两周写法等价
useSWR('/api/user', fetcher) useSWR('/api/user', url => fetcher(url))
- 如果需要传递额外的参数,需要使用数组结构,以下两种写法等价
const {data} = useSWR(['/api/user', 参数], fetcher); const { data } = useSWR(['/api/user', 参数], ([url, 参数]) => fetcher(url, 参数));
- 组件中实际使用时,可以自主拼接符合需要的 url 地址
import useSWR from "swr"; const fetcher = async (urls) => { const [url, token] = urls; return fetch(`${url}?token=${token}`).then((r) => r.json()); }; export default function Ajax() { const { data } = useSWR(["/api/user", "token"], fetcher); return ( <div className="Ajax"> {data && data.map((item) => <h5 key={item.name}>{item.name}</h5>)} </div> ); }
6. 分页请求
- 实现 上一页 和 下一页 的切换效果
import useSWR from "swr"; import { useState } from "react"; const fetcher = async (url) => fetch(url).then((r) => r.json()); export default function Ajax() { const [page, setPage] = useState(1); // 分页页码 const { data } = useSWR(`/api/daniu?page=${page}`, fetcher, { fallbackData: [], // revalidateOnFocus: false, // 窗口聚焦时自动重新请求。 // dedupingInterval: 2000, // 删除一段时间内相同 key 的重复请求(以毫秒为单位),默认2000。可以不配置,不配置在进行快速切换分页的时候,同一个url的请求在2秒以内不会重复发送。设置为0,每次点击分页按钮都会发送请求。 }); return ( <div className="Ajax"> <div className="btns"> <button onClick={() => setPage(page - 1)}>上一页</button> <button onClick={() => setPage(page + 1)}>下一页</button> </div> {data.course && data.course.data.map((item) => <h5 key={item.id}>{item.title}</h5>)} </div> ); }