SWR 请求库

fe8d0cd6ceca664d93262a624c2358a7.png

SWR 用于数据请求的 React Hooks 库,SWR 由 Next.js(React 框架)背后的同一团队创建。

npm install swr

“SWR” 这个名字来自于 stale-while-revalidate:一种由 HTTP RFC 5861 推广的 HTTP 缓存失效策略。这种策略首先从缓存中返回数据(过期的),同时发送 fetch 请求(重新验证),最后得到最新数据。

使用 SWR,组件将会不断地、自动获得最新数据流。UI 也会一直保持快速响应。

import useSWR from 'swr'


const fetcher = (...args) => fetch(...args).then((res) => res.json())


function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)


  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

该示例中,useSWR hook 接受一个字符串 key 和一个函数 fetcher。key 是数据的唯一标识符(通常是 API URL),并传递给 fetcher。fetcher 可以是任何返回数据的异步函数,你可以使用原生的 fetch 或 Axios 之类的工具。

基于请求的状态,这个 hook 返回 2 个值:data 和 error。

代码实现

了解了什么是 SWR 后,接下来看看如何实现它。

实现之前,先拆解下目标:

1.当请求数据时,首先从缓存中读取,并立即返回给调用者2.如果数据已经过期,则发起 fetch 请求,获取最新数据

我们需要用一个“容器”来缓存请求回来的复杂数据,在 JS 中,我们很容易第一时间想到使用 Object。

使用 Object 虽然没有什么问题,但它的结构是 “字符串—值” 的对应,只支持字符串作为键名。而在 ES6 中,Map 提供了 “值—值” 对应这种更完善的 Hash,更适合用于“键值对”这种数据结构。

为了方便代码实现后,有一个比较好的对比。这里先写一下不使用缓存时数据请求方式:

const data = await fetcher();

为了让 fetcher 支持数据缓存的能力,这里需要对 fetcher 进行一层封装。

封装之前,先定义一下需要被缓存的数据,那么什么数据需要被缓存呢?

很显然,不就是请求返回的数据吗。但与此同时,你也应该想到,如果重复调用函数,最好不要发送多次请求。

所以缓存数据中应该有:请求返回的数据,当前正在进行中的请求(如果有),避免多次请求。

const cache = new Map(); // 缓存数据


async function swr(cacheKey, fetcher) {
  // 首先从缓存中获取
  let data = cache.get(cacheKey) || { value: null, promise: null };
  // 写入缓存
  cache.set(cacheKey, data);


  // 没有数据且也没有在请求中,需要发送请求
  if (!data.value && !data.promise) {
    // 保存当前请求的 promise
    data.promise = fetcher()
      .then((val) => {
        data.value = val; // 请求成功,将数据存起来
      })
      .catch((err) => {
        console.log(err);
      })
      .finally(() => {
        data.promise = null; // 请求完毕,不再保存 promise
      });
  }


  // 没有数据,但正在请求中,复用保存的 promise
  if (data.promise && !data.value) await data.promise;
  // 返回数据
  return data.value;
}

这样,我们就实现了数据缓存的能力。

支持缓存过期时间,在已有缓存能力的基础上,再支持过期时间 cacheTime 就很容易了。只需要在发起新的请求前,判断下是否过期:

const isStaled = Date.now() - 获取到数据的时间 > cacheTime

所以,在缓存数据中我们还需要保存获取到数据的时间:

const cache = new Map();


// 新增 cacheTime 参数
async function swr(cacheKey, fetcher, cacheTime) {
  let data = cache.get(cacheKey) || { value: null, time: 0, promise: null };
  cache.set(cacheKey, data);


  // 是否过期
  const isStaled = Date.now() - data.time > cacheTime;
  // 已经过期了,且也没有在请求中,需要发送请求
  if (isStaled && !data.promise) {
    data.promise = fetcher()
      .then((val) => {
        data.value = val;
        data.time = Date.now(); // 保存获取到数据的时间
      })
      .catch((err) => {
        console.log(err);
      })
      .finally(() => {
        data.promise = null;
      });
  }


  if (data.promise && !data.value) await data.promise;
  return data.value;
}

有了以上的封装,调用方法变更为:

// before
const data = await fetcher();


// after
const data = await swr('cache-key', fetcher, 3000);

首次调用时,会通过接口请求数据。随后调用会立即返回缓存数据。如果调用间隔超过 3s,将先返回缓存数据,再请求接口获取最新的数据。

大功告成!我们用近 20 行代码简单实现了一套 SWR 机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值