当前位置: 首页 > 工具软件 > Next.js > 使用案例 >

nextjs系列教程(十一):nextjs 请求之 swr

谢豪
2023-12-01

SWR

  • 安装:yarn add swr

1. 语法介绍

next.js 团队创建了一个名为 swrreact hook,用于数据获取。如果您在客户端获取数据,我们强烈建议您使用这个 hook。它具有缓存,自动重新请求,窗口焦点跟踪,客户端周期性请求数据的功能。

  • api用法

    const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
    
    • useSWR 参数说明
      • key: 请求的唯一 key,类型可以是 stringfunctionarraynull
      • 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>
      );
    }
    
 类似资料: