• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alovajs / alova / #183

13 Jun 2024 08:41AM CUT coverage: 97.363% (+0.005%) from 97.358%
#183

push

github

web-flow
fix: remove slash at the end when sending with empty url(#384)

524 of 547 branches covered (95.8%)

Branch coverage included in aggregate %.

7 of 7 new or added lines in 1 file covered. (100.0%)

2688 of 2752 relevant lines covered (97.67%)

149.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.78
/src/functions/createRequestState.ts
1
import Method from '@/Method';
1✔
2
import { createHook } from '@/createHook';
1✔
3
import { getResponseCache } from '@/storage/responseCache';
1✔
4
import {
1✔
5
  _self,
1✔
6
  debounce,
1✔
7
  getContext,
1✔
8
  getHandlerMethod,
1✔
9
  getMethodInternalKey,
1✔
10
  isNumber,
1✔
11
  noop,
1✔
12
  promiseStatesHook,
1✔
13
  sloughConfig
1✔
14
} from '@/utils/helper';
1✔
15
import {
1✔
16
  deleteAttr,
1✔
17
  falseValue,
1✔
18
  forEach,
1✔
19
  isArray,
1✔
20
  isSSR,
1✔
21
  promiseCatch,
1✔
22
  pushItem,
1✔
23
  trueValue,
1✔
24
  undefinedValue
1✔
25
} from '@/utils/variables';
1✔
26
import {
1✔
27
  AlovaMethodHandler,
1✔
28
  CompleteHandler,
1✔
29
  EnumHookType,
1✔
30
  ErrorHandler,
1✔
31
  ExportedType,
1✔
32
  FetchRequestState,
1✔
33
  FetcherHookConfig,
1✔
34
  FrontRequestHookConfig,
1✔
35
  FrontRequestState,
1✔
36
  Progress,
1✔
37
  SuccessHandler,
1✔
38
  UseHookConfig,
1✔
39
  WatcherHookConfig
1✔
40
} from '~/typings';
1✔
41
import useHookToSendRequest from './useHookToSendRequest';
1✔
42

1✔
43
const refCurrent = <T>(ref: { current: T }) => ref.current;
1✔
44
/**
1✔
45
 * 创建请求状态,统一处理useRequest、useWatcher、useFetcher中一致的逻辑
1✔
46
 * 该函数会调用statesHook的创建函数来创建对应的请求状态
1✔
47
 * 当该值为空时,表示useFetcher进入的,此时不需要data状态和缓存状态
1✔
48
 * @param methodInstance 请求方法对象
1✔
49
 * @param useHookConfig hook请求配置对象
1✔
50
 * @param initialData 初始data数据
1✔
51
 * @param immediate 是否立即发起请求
1✔
52
 * @param watchingStates 被监听的状态,如果未传入,直接调用handleRequest
1✔
53
 * @param debounceDelay 请求发起的延迟时间
1✔
54
 * @returns 当前的请求状态、操作函数及事件绑定函数
1✔
55
 */
1✔
56
export default function createRequestState<
357✔
57
  S,
357✔
58
  E,
357✔
59
  R,
357✔
60
  T,
357✔
61
  RC,
357✔
62
  RE,
357✔
63
  RH,
357✔
64
  UC extends UseHookConfig<ARG>,
357✔
65
  ARG extends any[] = any[]
357✔
66
>(
357✔
67
  hookType: EnumHookType,
357✔
68
  methodHandler: Method<S, E, R, T, RC, RE, RH> | AlovaMethodHandler<S, E, R, T, RC, RE, RH, ARG>,
357✔
69
  useHookConfig: UC,
357✔
70
  initialData?: any,
357✔
71
  immediate = falseValue,
357✔
72
  watchingStates?: E[],
357✔
73
  debounceDelay: WatcherHookConfig<S, E, R, T, RC, RE, RH, ARG>['debounce'] = 0
357✔
74
) {
357✔
75
  // 动态获取methoHanlder参数
357✔
76
  // 复制一份config,防止外部传入相同useHookConfig导致vue2情况下的状态更新错乱问题
357✔
77
  useHookConfig = { ...useHookConfig };
357✔
78
  const statesHook = promiseStatesHook('useHooks'),
357✔
79
    {
357✔
80
      create,
357✔
81
      export: stateExport,
357✔
82
      effectRequest,
357✔
83
      update,
357✔
84
      memorize = _self,
357✔
85
      ref = val => ({ current: val })
357✔
86
    } = statesHook,
357✔
87
    middleware = useHookConfig.middleware;
357✔
88
  let initialLoading = middleware ? falseValue : !!immediate;
357✔
89

357✔
90
  // 当立即发送请求时,需要通过是否强制请求和是否有缓存来确定初始loading值,这样做有以下两个好处:
357✔
91
  // 1. 在react下立即发送请求可以少渲染一次
357✔
92
  // 2. SSR渲染的html中,其初始视图为loading状态的,避免在客户端展现时的loading视图闪动
357✔
93
  // 3. 如果config.middleware中设置了`controlLoading`时,需要默认为false,但这边无法确定middleware中是否有调用`controlLoading`,因此条件只能放宽点,当有`config.middleware`时则初始`loading`为false
357✔
94
  if (immediate && !middleware) {
357✔
95
    // 调用getHandlerMethod时可能会报错,需要try/catch
220✔
96
    try {
220✔
97
      const methodInstance = getHandlerMethod(methodHandler),
220✔
98
        alovaInstance = getContext(methodInstance),
220✔
99
        cachedResponse: R | undefined = getResponseCache(alovaInstance.id, getMethodInternalKey(methodInstance)),
220✔
100
        forceRequestFinally = sloughConfig(
220✔
101
          (useHookConfig as FrontRequestHookConfig<S, E, R, T, RC, RE, RH, ARG> | FetcherHookConfig<ARG>).force ??
220!
102
            falseValue
220✔
103
        );
220✔
104
      initialLoading = !!forceRequestFinally || !cachedResponse;
220✔
105
    } catch (error) {}
220!
106
  }
220✔
107

357✔
108
  const hookInstance = refCurrent(ref(createHook(hookType, useHookConfig))),
357✔
109
    progress: Progress = {
357✔
110
      total: 0,
357✔
111
      loaded: 0
357✔
112
    },
357✔
113
    // 将外部传入的受监管的状态一同放到frontStates集合中
357✔
114
    { managedStates = {} } = useHookConfig as FrontRequestHookConfig<S, E, R, T, RC, RE, RH, ARG>,
357✔
115
    frontStates = {
357✔
116
      ...managedStates,
357✔
117
      data: create(initialData, hookInstance),
357✔
118
      loading: create(initialLoading, hookInstance),
357✔
119
      error: create(undefinedValue as Error | undefined, hookInstance),
357✔
120
      downloading: create({ ...progress }, hookInstance),
357✔
121
      uploading: create({ ...progress }, hookInstance)
357✔
122
    },
357✔
123
    hasWatchingStates = watchingStates !== undefinedValue,
357✔
124
    // 初始化请求事件
357✔
125
    // 统一的发送请求函数
357✔
126
    handleRequest = (
357✔
127
      handler: Method<S, E, R, T, RC, RE, RH> | AlovaMethodHandler<S, E, R, T, RC, RE, RH, ARG> = methodHandler,
309✔
128
      sendCallingArgs?: [...ARG, ...any]
309✔
129
    ) => useHookToSendRequest(hookInstance, handler, sendCallingArgs),
357✔
130
    // 以捕获异常的方式调用handleRequest
357✔
131
    // 捕获异常避免异常继续向外抛出
357✔
132
    wrapEffectRequest = () => {
357✔
133
      promiseCatch(handleRequest(), noop);
272✔
134
    };
357✔
135

357✔
136
  /**
357✔
137
   * ## react ##每次执行函数都需要重置以下项
357✔
138
   **/
357✔
139
  hookInstance.fs = frontStates;
357✔
140
  hookInstance.sh = [];
357✔
141
  hookInstance.eh = [];
357✔
142
  hookInstance.ch = [];
357✔
143
  hookInstance.c = useHookConfig;
357✔
144
  // 在服务端渲染时不发送请求
357✔
145
  if (!isSSR) {
357✔
146
    effectRequest(
357✔
147
      {
357✔
148
        handler:
357✔
149
          // watchingStates为数组时表示监听状态(包含空数组),为undefined时表示不监听状态
357✔
150
          hasWatchingStates
357✔
151
            ? debounce(wrapEffectRequest, (changedIndex?: number) =>
357✔
152
                isNumber(changedIndex) ? (isArray(debounceDelay) ? debounceDelay[changedIndex] : debounceDelay) : 0
94✔
153
              )
357✔
154
            : wrapEffectRequest,
357✔
155
        removeStates: () => forEach(hookInstance.rf, fn => fn()),
357✔
156
        saveStates: (states: FrontRequestState) => forEach(hookInstance.sf, fn => fn(states)),
357✔
157
        frontStates: frontStates,
357✔
158
        watchingStates,
357✔
159
        immediate: immediate ?? trueValue
357!
160
      },
357✔
161
      hookInstance
357✔
162
    );
357✔
163
  }
357✔
164

357✔
165
  type PartialFrontRequestState = Partial<FrontRequestState<boolean, R, Error | undefined, Progress, Progress>>;
357✔
166
  type PartialFetchRequestState = Partial<FetchRequestState<boolean, Error | undefined, Progress, Progress>>;
357✔
167
  return {
357✔
168
    loading: stateExport(frontStates.loading, hookInstance) as unknown as ExportedType<boolean, S>,
357✔
169
    data: stateExport(frontStates.data, hookInstance) as unknown as ExportedType<R, S>,
357✔
170
    error: stateExport(frontStates.error, hookInstance) as unknown as ExportedType<Error | null, S>,
357✔
171
    get downloading() {
357✔
172
      hookInstance.ed = trueValue;
46✔
173
      return stateExport(frontStates.downloading, hookInstance) as unknown as ExportedType<Progress, S>;
46✔
174
    },
357✔
175
    get uploading() {
357✔
176
      hookInstance.eu = trueValue;
18✔
177
      return stateExport(frontStates.uploading, hookInstance) as unknown as ExportedType<Progress, S>;
18✔
178
    },
357✔
179
    onSuccess(handler: SuccessHandler<S, E, R, T, RC, RE, RH, ARG>) {
357✔
180
      pushItem(hookInstance.sh, handler);
281✔
181
    },
357✔
182
    onError(handler: ErrorHandler<S, E, R, T, RC, RE, RH, ARG>) {
357✔
183
      pushItem(hookInstance.eh, handler);
59✔
184
    },
357✔
185
    onComplete(handler: CompleteHandler<S, E, R, T, RC, RE, RH, ARG>) {
357✔
186
      pushItem(hookInstance.ch, handler);
16✔
187
    },
357✔
188
    update: memorize((newStates: PartialFrontRequestState | PartialFetchRequestState) => {
357✔
189
      // 当useFetcher调用时,其fetching使用的是loading,更新时需要转换过来
5✔
190
      const { fetching } = newStates as PartialFetchRequestState;
5✔
191
      if (fetching) {
5✔
192
        (newStates as PartialFrontRequestState).loading = fetching;
1✔
193
        deleteAttr(newStates as PartialFetchRequestState, 'fetching');
1✔
194
      }
1✔
195
      update(newStates, frontStates, hookInstance);
5✔
196
    }),
357✔
197
    abort: memorize(() => hookInstance.m && hookInstance.m.abort()),
357✔
198

357✔
199
    /**
357✔
200
     * 通过执行该方法来手动发起请求
357✔
201
     * @param sendCallingArgs 调用send函数时传入的参数
357✔
202
     * @param methodInstance 方法对象
357✔
203
     * @param isFetcher 是否为isFetcher调用
357✔
204
     * @returns 请求promise
357✔
205
     */
357✔
206
    send: memorize((sendCallingArgs?: [...ARG, ...any], methodInstance?: Method<S, E, R, T, RC, RE, RH>) =>
357✔
207
      handleRequest(methodInstance, sendCallingArgs)
37✔
208
    ),
357✔
209

357✔
210
    /** 为兼容options框架,如vue2、原生小程序等,将config对象原样导出 */
357✔
211
    _$c: useHookConfig
357✔
212
  };
357✔
213
}
357✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc