YangYuGang
2025-03-11 7462fd192326d7cf3418b6185ca437b2667cbeab
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { onUnmounted, ref, watchEffect } from 'vue';
 
import type { UseRequestPlugin } from '../types';
import type { CachedData } from '../utils/cache';
import { getCache, setCache } from '../utils/cache';
import { getCachePromise, setCachePromise } from '../utils/cachePromise';
import { subscribe, trigger } from '../utils/cacheSubscribe';
 
const useCachePlugin: UseRequestPlugin<any, any[]> = (
  fetchInstance,
  {
    cacheKey,
    cacheTime = 5 * 60 * 1000,
    staleTime = 0,
    setCache: customSetCache,
    getCache: customGetCache,
  },
) => {
  const unSubscribeRef = ref<() => void>();
  const currentPromiseRef = ref<Promise<any>>();
 
  const _setCache = (key: string, cachedData: CachedData) => {
    customSetCache ? customSetCache(cachedData) : setCache(key, cacheTime, cachedData);
    trigger(key, cachedData.data);
  };
 
  const _getCache = (key: string, params: any[] = []) => {
    return customGetCache ? customGetCache(params) : getCache(key);
  };
 
  watchEffect(() => {
    if (!cacheKey) return;
 
    // get data from cache when init
    const cacheData = _getCache(cacheKey);
    if (cacheData && Object.hasOwnProperty.call(cacheData, 'data')) {
      fetchInstance.state.data = cacheData.data;
      fetchInstance.state.params = cacheData.params;
 
      if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
        fetchInstance.state.loading = false;
      }
    }
 
    // subscribe same cachekey update, trigger update
    unSubscribeRef.value = subscribe(cacheKey, (data) => {
      fetchInstance.setState({ data });
    });
  });
 
  onUnmounted(() => {
    unSubscribeRef.value?.();
  });
 
  if (!cacheKey) {
    return {};
  }
 
  return {
    onBefore: (params) => {
      const cacheData = _getCache(cacheKey, params);
 
      if (!cacheData || !Object.hasOwnProperty.call(cacheData, 'data')) {
        return {};
      }
 
      // If the data is fresh, stop request
      if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
        return {
          loading: false,
          data: cacheData?.data,
          error: undefined,
          returnNow: true,
        };
      } else {
        // If the data is stale, return data, and request continue
        return { data: cacheData?.data, error: undefined };
      }
    },
    onRequest: (service, args) => {
      let servicePromise = getCachePromise(cacheKey);
 
      // If has servicePromise, and is not trigger by self, then use it
      if (servicePromise && servicePromise !== currentPromiseRef.value) {
        return { servicePromise };
      }
 
      servicePromise = service(...args);
      currentPromiseRef.value = servicePromise;
      setCachePromise(cacheKey, servicePromise);
 
      return { servicePromise };
    },
    onSuccess: (data, params) => {
      if (cacheKey) {
        // cancel subscribe, avoid trgger self
        unSubscribeRef.value?.();
 
        _setCache(cacheKey, { data, params, time: new Date().getTime() });
 
        // resubscribe
        unSubscribeRef.value = subscribe(cacheKey, (d) => {
          fetchInstance.setState({ data: d });
        });
      }
    },
    onMutate: (data) => {
      if (cacheKey) {
        // cancel subscribe, avoid trigger self
        unSubscribeRef.value?.();
 
        _setCache(cacheKey, {
          data,
          params: fetchInstance.state.params,
          time: new Date().getTime(),
        });
 
        // resubscribe
        unSubscribeRef.value = subscribe(cacheKey, (d) => {
          fetchInstance.setState({ data: d });
        });
      }
    },
  };
};
 
export default useCachePlugin;