SWR(Stale While Revalidate) 是指在进行数据请求的时候优先检查缓存数据,若数据尚在有效期则直接返回数据。若没有缓存数据或者数据过期则再去请求接口,接口返回值返回后再将其存到缓存中。

这里我们就涉及到几个部分:

  • 缓存策略
  • 过期时间
  • 请求函数

    简单的SWR封装

    ```typescript type Fetcher = () => Promise

const cache = new Map()

async function swr(cacheKey: string, fetcher: Fetcher, cacheTime: number) { const 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 }

  1. 这里,我们使用 Date.now() 来获取当前的时间戳,计算过后来与输入的 cacheTime 来进行对比。然后附加对 data.promise 的判断来进行数据的获取。
  2. <a name="jnX4r"></a>
  3. ### 更新缓存算法
  4. 针对于缓存算法,上一个例子我们只是对数据进行了一个简单的 map 缓存,这对于实际使用是不满足的。在实际使用中,我们还需要考虑缓存的数据量以及数据优先情况。这里我们可以封装一个简单的 LRU 算法。
  5. ```typescript
  6. class LRUCache {
  7. public capacity: number
  8. public cache: Map<any, any>
  9. constructor(capacity: number) {
  10. this.capacity = capacity
  11. this.cache = new Map()
  12. }
  13. get(key: any) {
  14. if (this.cache.has(key)) {
  15. const temp = this.cache.get(key)
  16. this.cache.delete(key)
  17. this.cache.set(key, temp)
  18. return temp
  19. }
  20. return undefined
  21. }
  22. set(key: any, val: any) {
  23. if (this.cache.has(key)) {
  24. // 先删除再加入
  25. this.cache.delete(key)
  26. } else if (this.cache.size >= this.capacity) {
  27. // 删除首值,即最不常用的值
  28. this.cache.delete(this.cache.keys().next().value)
  29. }
  30. this.cache.set(key, val)
  31. }
  32. }

添加条件运算

  1. type ShouldCache = () => any | string | undefined
  2. async function swr(cacheKey: ShouldCache) {
  3. // ...
  4. const cKey = typeof cacheKey === 'function' ? cacheKey() : cacheKey
  5. if (cKey) {
  6. // ...
  7. } else {
  8. return fetcher()
  9. }
  10. }

根据 cacheKey 的值来进行判断,看是否需要使用缓存。

组合

  1. type Fetcher = () => Promise<any>
  2. type shouldCache = () => any | string | undefined
  3. // LRU cache
  4. export class LRUCache {
  5. public capacity: number
  6. public cache: Map<any, any>
  7. constructor(capacity: number) {
  8. this.capacity = capacity
  9. this.cache = new Map()
  10. }
  11. get(key: any) {
  12. if (this.cache.has(key)) {
  13. const temp = this.cache.get(key)
  14. this.cache.delete(key)
  15. this.cache.set(key, temp)
  16. return temp
  17. }
  18. return undefined
  19. }
  20. set(key: any, val: any) {
  21. if (this.cache.has(key)) {
  22. this.cache.delete(key)
  23. } else if (this.cache.size >= this.capacity)
  24. this.cache.delete(this.cache.keys().next().value)
  25. this.cache.set(key, val)
  26. }
  27. }
  28. const cache = new LRUCache(50)
  29. export async function swr(
  30. cacheKey: shouldCache,
  31. fetcher: Fetcher,
  32. cacheTime: number
  33. ) {
  34. const cKey = typeof cacheKey === 'function' ? cacheKey() : cacheKey
  35. if (cKey) {
  36. const data = cache.get(cacheKey) || { value: null, time: 0, promise: null }
  37. cache.set(cKey, data)
  38. const isStaled = Date.now() - data.time > cacheTime
  39. if (isStaled && !data.promise) {
  40. data.promise = fetcher()
  41. .then((val) => {
  42. data.value = val
  43. data.time = Date.now()
  44. })
  45. .catch((err) => {
  46. console.error(err)
  47. })
  48. .finally(() => {
  49. data.promise = null
  50. })
  51. }
  52. if (data.promise && !data.value) await data.promise
  53. return data.value
  54. } else {
  55. return fetcher()
  56. }
  57. }

这样我们就完成了一个简单的 SWR 请求函数。