swr源码解析

800 阅读3分钟

数据状态关系

组件内状态和缓存数据是双向数据绑定的关系

全局状态

  • CONCURRENT_PROMISES, 当前正在进行中的请求
  • CONCURRENT_PROMISES_TS,当前正在进行中的请求的开始时间
  • FOCUS_REVALIDATORS,窗口聚焦时,需要触发的事件队列
  • CACHE_REVALIDATORS,缓存更新时,需要触发的事件队列
  • MUTATION_TS
  • cache

全局方法

trigger(_key, shouldRevalidate = true)

_key序列化为key 从CACHE_REVALIDATORS获取与key对应的updaters,从cache中取出与key对应的currentData/currentError,遍历updaters,调用updater(shouldRevalidate, currentData, currentError, i>0)

broadcastState(key, data, error)

从CACHE_REVALIDATORS中获取与key对应的updaters,遍历updaters,调用updater(false, data, error)

mutate(_key, _data, shouldRevalidate = true)

_key序列化为key data为undefined, 调用trigger(_key, shouldRevalidate) MUTATION_TS[key] = Date,now() - 1 根据_data的类型做处理,最后获得数据data data !== undefined, cache.set(key, data, false) 从CACHE_REVALIDATORS中获取与key对应的updaters,遍历updaters,调用updater(!!shouldRevalidate, data, error, i > 0)

useSWR内部方法

dispatch(payload)

使用payload更新stateRef 根据stateDependencies中对应key值的true/false,决定是否重新渲染 shouldUpdateState || config.suspense,重新渲染

boundMutate(data, shouldRevalidate)

内部调用mutate(key, data, shouldRevalidate),指定key值mutate

revalidate(revalidateOpts: { retryCount?: number, dedupe?: boolean})

核心逻辑,根据dedupe决定并行请求策略: dedupe = true

  • 忽略此次发起的请求,设置startAt为进行中请求的开始时间,
  • newData为进行中请求的响应结果

dedupe = false

  • 如果存在进行中的请求,在MUTATION_TS中记录当前时间
  • 处理loading
  • 调用fn,并赋值到CONCURRENT_PROMISES[key]
  • 设置startAt为当前时间,并赋值给CONCURRENT_PROMISES_TS[key]
  • 赋值newData
  • 设置定时器,dedupingInterval时间后,清除CONCURRENT_PROMISES[key]和CONCURRENT_PROMISES_TS[key]
  • 调用onSuccess函数

判断MUTATION_TS[key],如果存在且比startAt大,则设置isValidating为false,并放弃对结果的处理。(注意连续2次触发revalidate的时间间隔小于1ms,可能会导致响应覆盖的情况 req1 - 5s - res1/req2 - 3s - res2,这种情况下,res1会覆盖res2)

将newData存入到缓存,keyErr对应的缓存设置为undefined 一系列对状态的判断,dispatch(newState) 根据shouldDeduping决定是否执行broadcastState(key, newData, err);

错误处理

  • 保存错误信息到缓存
  • 更新state
  • 根据shouldDeduping决定是否调用broadcastState(key, undefined, err)
  • 调用onError方法
  • 错误时重试策略

设置loading为false。

onMounted处理

  • 初始化数据,优先取缓存
  • 定义softeRevalidate, 默认dedupe = true 调用revalidate
  • 有条件的,执行自动revalidate,此处做了一个优化:优先使用requestIdleCallback调用softRevalidate。
  • 对softRevalidate进行节流处理,加入 FOCUS_REVALIDATORS[key]的队列中
  • 定义onUpdate函数,用于缓存数据更新时,触发组件内部状态更新
  • 将onUpdate添加到CACHE_REVALIDATORS[key]的队列中
  • 定义清除工作

轮询相关处理

  • 定义tick,用于调用revalidate方法
  • 根据config.refreshInterval的值,判断是否需要轮询 这里有个坑,如果定义revalidateOnMount为false,但是定义 refreshInterval为2000,依然会自动触发请求

非Suspense返回

  • 定义state = { revalidate, mutate, boundMutate}
  • 定义error, data, isValidating为state的访问器属性,用来捕获依赖(妥妥的vue实现)

config.ts做了什么?

  • 定义全局cache
  • 定义全局状态管理
  • 定义默认参数
  • 监听窗口显示/隐藏/聚焦事件