Skip to content

Commit 9b2f789

Browse files
committed
feat: extra use-request package
1 parent 4f07218 commit 9b2f789

28 files changed

+1444
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@vue-hooks-plus/vitepress-demo-block": "^1.3.1",
3939
"@vue-hooks-plus/use-immer":"workspace:^1.0.0",
4040
"@vue-hooks-plus/use-worker":"workspace:^1.0.0",
41+
"@vue-hooks-plus/use-request":"workspace:^1.0.0",
4142
"@vue/test-utils": "^2.1.0",
4243
"@vitest/coverage-c8":"^0.25.7",
4344
"eslint": "^8.20.0",

packages/hooks/docs/.vitepress/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ export default defineConfig({
130130
icon: 'github',
131131
link: 'https://github.com//InhiblabCore/vue-hooks-plus',
132132
},
133+
{
134+
icon: 'discord',
135+
link: 'https://discord.gg/RU6ZPjf8',
136+
},
133137
{
134138
icon: 'twitter',
135139
link: 'https://twitter.com/Yong_Git',

packages/use-request/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "@vue-hooks-plus/use-request",
3+
"version": "1.0.0",
4+
"description": "Vue use-request hooks library",
5+
"files": [
6+
"dist",
7+
"LICENSE",
8+
"package.json",
9+
"README.md"
10+
],
11+
"main": "./dist/useRequest.cjs.js",
12+
"module": "./dist/useRequest.es.js",
13+
"types":"./dist/types/index.d.ts" ,
14+
"scripts": {
15+
"build": "vite build"
16+
},
17+
"keywords": [],
18+
"dependencies": {
19+
"lodash": "^4.17.21"
20+
},
21+
"repository": "https://github.com/InhiblabCore/vue-hooks-plus",
22+
"homepage": "https://github.com/InhiblabCore/vue-hooks-plus",
23+
"author": "NelsonYong",
24+
"license": "MIT"
25+
}

packages/use-request/src/Fetch.ts

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import { Ref, unref, watchEffect } from 'vue'
2+
import {
3+
UseRequestFetchState,
4+
UseRequestOptions,
5+
UseRequestPluginReturn,
6+
UseRequestService,
7+
} from './types'
8+
9+
export default class Fetch<TData, TParams extends unknown[] = any> {
10+
pluginImpls: UseRequestPluginReturn<TData, TParams>[] | undefined
11+
12+
count = 0
13+
14+
state: UseRequestFetchState<TData, TParams> = {
15+
loading: false,
16+
params: undefined,
17+
data: undefined,
18+
error: undefined,
19+
}
20+
21+
constructor(
22+
public serviceRef: Ref<UseRequestService<TData, TParams>>,
23+
public options: UseRequestOptions<TData, TParams, any>,
24+
public setUpdateData: (s: any, key?: keyof UseRequestFetchState<TData, TParams>) => void,
25+
public initState: Partial<UseRequestFetchState<TData, TParams>> = {},
26+
) {
27+
this.state = {
28+
...this.state,
29+
loading: !options.manual,
30+
...initState,
31+
}
32+
}
33+
34+
// 设置state
35+
setState(s: Partial<UseRequestFetchState<TData, TParams>> = {}) {
36+
this.state = {
37+
...this.state,
38+
...s,
39+
}
40+
this.setUpdateData(this.state)
41+
}
42+
43+
/**
44+
*
45+
* @param data Result value `any`
46+
* @param key Result key `data`| `params` | `loading`| `error`
47+
*/
48+
setData(
49+
data: any,
50+
key?:
51+
| keyof UseRequestFetchState<TData, TParams>
52+
| (keyof UseRequestFetchState<TData, TParams>)[],
53+
) {
54+
if (key instanceof Array) {
55+
key.forEach(k => {
56+
this.state[k as keyof UseRequestFetchState<TData, TParams>] = data
57+
this.setUpdateData(data, k)
58+
})
59+
} else {
60+
this.state[key as keyof UseRequestFetchState<TData, TParams>] = data
61+
this.setUpdateData(data, key)
62+
}
63+
}
64+
65+
// 遍历需要运行的插件,是一个回调函数,供插件获取fetch实例和在对应节点执行插件逻辑
66+
runPluginHandler(event: keyof UseRequestPluginReturn<TData, TParams>, ...rest: any[]) {
67+
// @ts-ignore
68+
const r = (this.pluginImpls?.map(i => i[event]?.(...rest)) ?? [])?.filter(Boolean)
69+
// @ts-ignore
70+
return Object.assign({}, ...r)
71+
}
72+
73+
// 异步请求
74+
// @ts-ignore
75+
async runAsync(...params: TParams): Promise<TData> {
76+
this.count += 1
77+
const currentCount = this.count
78+
const { stopNow = false, returnNow = false, ...state } = this.runPluginHandler(
79+
'onBefore',
80+
params,
81+
)
82+
// 是否停止请求
83+
if (stopNow) {
84+
return new Promise(() => {})
85+
}
86+
87+
this.setState({
88+
loading: true,
89+
params,
90+
...state,
91+
})
92+
93+
// 是否立刻返回
94+
if (returnNow) {
95+
return Promise.resolve(state.data)
96+
}
97+
98+
// 请求前返回
99+
this.options.onBefore?.(params)
100+
101+
try {
102+
// replace service 开始请求,如果含有onRequest事件名
103+
let { servicePromise } = this.runPluginHandler('onRequest', this.serviceRef.value, params)
104+
105+
const requestReturn = (res: any) => {
106+
// 取消了请求,count将与currentCount不一致,将发送空请求
107+
if (currentCount !== this.count) {
108+
return new Promise(() => {})
109+
}
110+
// 格式化数据
111+
const formattedResult = this.options.formatResult ? this.options.formatResult(res) : res
112+
113+
this.setState({
114+
data: formattedResult,
115+
error: undefined,
116+
loading: false,
117+
})
118+
// 请求成功
119+
this.options.onSuccess?.(formattedResult, params)
120+
121+
this.runPluginHandler('onSuccess', formattedResult, params)
122+
123+
// 无论请求成功还是失败都执行
124+
this.options.onFinally?.(params, formattedResult, undefined)
125+
126+
if (currentCount === this.count) {
127+
this.runPluginHandler('onFinally', params, formattedResult, undefined)
128+
}
129+
130+
return formattedResult
131+
}
132+
133+
if (!servicePromise) {
134+
/** 自动依赖收集 */
135+
if (!this.options.manual && this.options.refreshDeps === true) {
136+
watchEffect(async () => {
137+
if (unref(this.options.ready)) {
138+
this.setData(true, 'loading')
139+
servicePromise = this.serviceRef.value(...params)
140+
const res = await servicePromise
141+
return requestReturn(res)
142+
}
143+
})
144+
} else {
145+
servicePromise = this.serviceRef.value(...params)
146+
}
147+
}
148+
const res = await servicePromise
149+
return requestReturn(res)
150+
} catch (error) {
151+
if (currentCount !== this.count) {
152+
return new Promise(() => {})
153+
}
154+
155+
this.setState({
156+
error,
157+
loading: false,
158+
})
159+
160+
this.options.onError?.(error as Error, params)
161+
this.runPluginHandler('onError', error, params)
162+
163+
// 无论请求成功还是失败都执行
164+
this.options.onFinally?.(params, undefined, error as Error)
165+
166+
if (currentCount === this.count) {
167+
this.runPluginHandler('onFinally', params, undefined, error)
168+
}
169+
170+
throw error
171+
}
172+
}
173+
174+
run(...params: TParams) {
175+
this.runAsync(...params).catch(error => {
176+
if (!this.options.onError) {
177+
console.error(error)
178+
}
179+
})
180+
}
181+
182+
cancel() {
183+
this.count += 1
184+
this.setState({
185+
loading: false,
186+
})
187+
188+
this.runPluginHandler('onCancel')
189+
}
190+
191+
refresh() {
192+
this.run(...((this.state.params || []) as TParams))
193+
}
194+
195+
refreshAsync() {
196+
return this.runAsync(...((this.state.params || []) as TParams))
197+
}
198+
199+
mutate(data?: TData | ((oldData?: TData) => TData | undefined)) {
200+
let targetData: TData | undefined
201+
if (typeof data === 'function') {
202+
// @ts-ignore
203+
targetData = data?.(this.state.data)
204+
} else {
205+
targetData = data
206+
}
207+
208+
this.runPluginHandler('onMutate', targetData)
209+
this.setState({
210+
data: targetData,
211+
})
212+
}
213+
}

packages/use-request/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import useRequest from "./useRequest";
2+
import { clearCache } from "./utils/cache";
3+
4+
export { clearCache };
5+
6+
export default useRequest;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { unref, ref, watch, watchEffect } from 'vue'
2+
import { UseRequestFetchState, UseRequestPlugin } from '../types'
3+
4+
// support refreshDeps & ready
5+
const useAutoRunPlugin: UseRequestPlugin<unknown, unknown[]> = (
6+
fetchInstance,
7+
{ manual, ready = true, refreshDeps = [], refreshDepsAction },
8+
) => {
9+
const hasAutoRun = ref(false)
10+
11+
watchEffect(() => {
12+
if (!manual) hasAutoRun.value = unref(ready)
13+
})
14+
15+
if (refreshDeps instanceof Array)
16+
watch(
17+
[hasAutoRun, ...refreshDeps],
18+
([autoRun]) => {
19+
if (!autoRun) return
20+
if (!manual && autoRun) {
21+
if (refreshDepsAction) {
22+
refreshDepsAction()
23+
} else {
24+
fetchInstance.refresh()
25+
}
26+
}
27+
},
28+
{
29+
deep: true,
30+
immediate: false,
31+
},
32+
)
33+
else
34+
watch(hasAutoRun, h => {
35+
if (!manual && h) {
36+
if (refreshDepsAction) {
37+
refreshDepsAction()
38+
} else {
39+
fetchInstance.refresh()
40+
}
41+
}
42+
})
43+
44+
return {
45+
onBefore: () => {
46+
if (!unref(ready)) {
47+
return {
48+
stopNow: true,
49+
}
50+
}
51+
},
52+
}
53+
}
54+
55+
useAutoRunPlugin.onInit = ({ initialData, ready = true, manual }) => {
56+
return {
57+
loading: (!manual && unref(ready)) as UseRequestFetchState<any, any[]>['loading'],
58+
}
59+
}
60+
61+
export default useAutoRunPlugin

0 commit comments

Comments
 (0)