vue-asyncx 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +347 -20
- package/dist/vue-asyncx.d.ts +2 -2
- package/dist/vue-asyncx.js +45 -45
- package/dist/vue-asyncx.umd.cjs +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,40 +1,64 @@
|
|
|
1
1
|
## 特性
|
|
2
2
|
|
|
3
|
-
-
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
3
|
+
- 效率+:异步相关样板代码减少50%
|
|
4
|
+
- 效率+:自动处理异步操作的乱序响应
|
|
5
|
+
- 可读+:异步操作命名指向明确、风格统一
|
|
6
|
+
- 可读+:易于写出符合“干净架构”理念的函数
|
|
7
|
+
- 质量+:全 TS 支持 + 100% 单元测试
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
### 安装
|
|
9
|
+
## 安装
|
|
12
10
|
|
|
13
11
|
```console
|
|
14
12
|
pnpm i vue-asyncx
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
## 使用
|
|
16
|
+
|
|
17
|
+
### useAsync 异步函数
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
当需要执行一个异步函数,可以将要执行的异步函数传入 `useAsync` 中,得到
|
|
20
|
+
|
|
21
|
+
- 一个异步函数的包裹函数
|
|
22
|
+
- 一个记录异步函数执行 `loading` 响应式数据
|
|
23
|
+
- 等等
|
|
20
24
|
|
|
21
25
|
```ts
|
|
22
|
-
import { submitOrder } from '@/api'
|
|
23
26
|
import { useAsync } from 'vue-asyncx'
|
|
24
27
|
|
|
25
|
-
/**
|
|
26
|
-
* 调用 useAsync('submit', ...),
|
|
27
|
-
* submit、submitLoading 自动提示
|
|
28
|
-
*/
|
|
29
28
|
const {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} = useAsync(
|
|
29
|
+
method,
|
|
30
|
+
methodLoading,
|
|
31
|
+
} = useAsync(async (a: number, b: number) => a + b)
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
method(1, 1).then(total => console.log(total))
|
|
35
34
|
```
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
如果不喜欢默认解构出来的 `method` 与 `methodLoading`,除了解构赋值的重命名,你还可以直接:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { useAsync } from 'vue-asyncx'
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
sum,
|
|
43
|
+
sumLoading
|
|
44
|
+
} = useAsync('sum', async (a: number, b: number) => a + b)
|
|
45
|
+
|
|
46
|
+
sum(1, 1).then(total => console.log(total))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
当你将 `sum` 字符串传入 `useAsync`,结果属性自动替换为 `sum` 和 `sumLoading`,并且,**解构附带 ts 提示**,在结果对象内输入 `s`,相关内容会自动提示。
|
|
50
|
+
|
|
51
|
+
同时,解构出的 `sum` TS 类型由传入的函数推导出,二者 TS 类型一致。
|
|
52
|
+
|
|
53
|
+
想象一下在多人合作的大型项目中,通过这种方式约束命名:不同开发人员可以自然地将相同模式的变量名关联在一起,互相理解代码、review 内容变得更简单。
|
|
54
|
+
|
|
55
|
+
### useAsyncData 异步数据
|
|
56
|
+
|
|
57
|
+
除了异步函数,另一个常见场景是异步数据。
|
|
58
|
+
|
|
59
|
+
假设你在开发一个用户详情页,页面通过 `user` 数据渲染,`user` 数据通过 `getUserById` 的 api 获取。
|
|
60
|
+
|
|
61
|
+
显然,`user` 是一个异步数据,通过 `useAsyncData`,你可以很容易处理这些内容:
|
|
38
62
|
|
|
39
63
|
```ts
|
|
40
64
|
import { getUserById } from '@/api'
|
|
@@ -50,3 +74,306 @@ const {
|
|
|
50
74
|
queryUser
|
|
51
75
|
} = useAsyncData('user', () => getUserById('1'), { immediate: true })
|
|
52
76
|
```
|
|
77
|
+
|
|
78
|
+
代码编写流程与思路流程高度一致:
|
|
79
|
+
- 数据命名:页面需要使用异步数据 user => `useAsyncData('user', `
|
|
80
|
+
- 数据获取:user 数据来自 getUserById 接口 => `useAsyncData('user', () => getUserById('1')`
|
|
81
|
+
- 触发获取:进入页面后需要立即获取 user 数据 `useAsyncData('user', () => getUserById('1'), { immediate: true })`
|
|
82
|
+
|
|
83
|
+
ok,写完上述,页面所需的 `user` 各种相关数据、函数立即出现,想用什么解构什么:
|
|
84
|
+
|
|
85
|
+
- `user` 响应式变量自动定义并声明
|
|
86
|
+
- 获取 `user` 的函数被自动命名为 `queryUser`
|
|
87
|
+
- 调用 `queryUser` 时的加载状态自动关联到 `queryUserLoading`
|
|
88
|
+
|
|
89
|
+
只需要在 TS 的提示下,按需解构出页面所需的内容
|
|
90
|
+
|
|
91
|
+
> `useAsyncData` 底层使用 `useAsync`,即 `useAsync` 的所有能力 `useAsyncData` 都具备。
|
|
92
|
+
> 除 `useAsync` 的能力外,`useAsyncData` 额外支持了一些与数据相关的能力。
|
|
93
|
+
> 二者区别:
|
|
94
|
+
> `useAsync` 关注异步函数,不关注需要长久保留结果值。例如 `submit`、`confirm` 等操作
|
|
95
|
+
> `useAsyncData` 则关注异步数据,异步函数则是数据的获取方式
|
|
96
|
+
|
|
97
|
+
### immediate 与 watch
|
|
98
|
+
|
|
99
|
+
注意到上面例子里的 `useAsyncData` 使用了 `immediate` 配置,效果是立即调用 `queryUser`
|
|
100
|
+
|
|
101
|
+
这个配置来自 vue `watch`,除了 `immediate` 外,**`useAsync` 和 `useAsyncData`** 完整支持了 vue watch 的各种配置
|
|
102
|
+
- 通过 `options.watch` 配置 watch source
|
|
103
|
+
- 通过 `options.watchOptions` 配置 watch 的其它 options
|
|
104
|
+
|
|
105
|
+
比如常见的通过 props 传递参数,props 参数改变,触发异步数据改变:
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
const {
|
|
109
|
+
user,
|
|
110
|
+
queryUserLoading,
|
|
111
|
+
queryUser
|
|
112
|
+
} = useAsyncData('user', () => getUserById(props.id), {
|
|
113
|
+
watch: () => props.id, // 此处用 props 举例,支持所有 vue watch source 的用法
|
|
114
|
+
immediate: true,
|
|
115
|
+
watchOptions: {
|
|
116
|
+
once: true // 此处用 once 举例,支持所有 vue watch options 内的配置
|
|
117
|
+
// 这里也可以配置 immediate,与上一层的 immediate 效果一致,优先级更高
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
实际上即使不配置 `options.watch`,配置 `options.immediate = true` 后内部也是通过 watch 机制触发的立即调用
|
|
123
|
+
|
|
124
|
+
默认情况下,由 watch 触发的调用不会传递任何参数。watch 的 handler 相当于:`() => queryUser()`。但在 vue 中,watch 的 handler 可以接收新、旧数据以及 `onCleanup`
|
|
125
|
+
|
|
126
|
+
`useAsync` 和 `useAsyncData` 可以通过配置 `options.watchOptions.handlerCreator` 来自定义 watch handler:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
const {
|
|
130
|
+
user,
|
|
131
|
+
queryUserLoading,
|
|
132
|
+
queryUser
|
|
133
|
+
} = useAsyncData('user', (id) => getUserById(id), {
|
|
134
|
+
watch: () => props.id,
|
|
135
|
+
immediate: true,
|
|
136
|
+
watchOptions: {
|
|
137
|
+
// fn 等价于 queryUser
|
|
138
|
+
handlerCreator(fn) {
|
|
139
|
+
// handlerCreator 需要返回 watch 的 handler
|
|
140
|
+
return (newId, oldId, onCleanup) => {
|
|
141
|
+
// handler 用法与 vue watch handler 一致,可以排除某些非法调用
|
|
142
|
+
if (!newId) return
|
|
143
|
+
fn(newId)
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 调用报错与参数
|
|
151
|
+
|
|
152
|
+
除了 `loading`,`useAsync` 与 `useAsyncData` 还支持记录调用的 `error` 与 `arguments`,默认状态下,它们的命名是:
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
const {
|
|
156
|
+
methodError,
|
|
157
|
+
methodArguments,
|
|
158
|
+
} = useAsync(() => {})
|
|
159
|
+
|
|
160
|
+
const {
|
|
161
|
+
queryDataError,
|
|
162
|
+
queryDataArguments
|
|
163
|
+
} = useAsyncData(() => {})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
与之前例子一致,当自定义命名时,结果随之改变:
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
const {
|
|
170
|
+
confirmError,
|
|
171
|
+
confirmArguments,
|
|
172
|
+
} = useAsync('confirm', () => {})
|
|
173
|
+
|
|
174
|
+
const {
|
|
175
|
+
queryUserError,
|
|
176
|
+
queryUserArguments
|
|
177
|
+
} = useAsyncData('user', () => {})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`error`
|
|
181
|
+
- **最后一次**调用 fn,**且**调用结果失败
|
|
182
|
+
- 记录异步 fn 的 reject 内容
|
|
183
|
+
- 记录同步 fn 的 throw 内容
|
|
184
|
+
- **新一次**调用开始后,`error` 自动置空
|
|
185
|
+
|
|
186
|
+
`arguments`
|
|
187
|
+
- 与 `loading` 一致,调用开始时记录本次调用传入的参数
|
|
188
|
+
- 调用结果出现后,`arguments` 自动置空
|
|
189
|
+
|
|
190
|
+
### 异步函数乱序响应
|
|
191
|
+
|
|
192
|
+
```js
|
|
193
|
+
// 使用 useAsync,没有 user 变量
|
|
194
|
+
const {
|
|
195
|
+
queryUser,
|
|
196
|
+
queryUserLoading,
|
|
197
|
+
} = useAsync('queryUser', () => {})
|
|
198
|
+
|
|
199
|
+
// 或者,使用 useAsyncData
|
|
200
|
+
const {
|
|
201
|
+
user,
|
|
202
|
+
queryUser,
|
|
203
|
+
queryUserLoading
|
|
204
|
+
} = useAsyncData('user', () => {})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`queryUser` 是个异步函数,由于异步操作时间不定,多次调用情况下会出现乱序响应,考虑下列的时间线顺序:
|
|
208
|
+
|
|
209
|
+
- **首次调用** `queryUser`,记做 `queryUser1`
|
|
210
|
+
- `queryUser1` 未结束,**再次调用** `queryUser`,记做 `queryUser2`
|
|
211
|
+
- `queryUser2` 先结束
|
|
212
|
+
- `queryUser1` 后结束
|
|
213
|
+
|
|
214
|
+
即:**依次调用 `queryUser1` 和 `queryUser2`,但 `queryUser2` 先于 `queryUser1` 结束**。这种场景很常见,专门处理很麻烦。
|
|
215
|
+
|
|
216
|
+
而使用 `useAsync` 和 `useAsyncData`,你无需再考虑这类问题,方法会自动帮你处理。
|
|
217
|
+
|
|
218
|
+
以上述例子举例:
|
|
219
|
+
|
|
220
|
+
- `queryUser1` 开始时,`queryUserLoading.value` 为 `true`
|
|
221
|
+
- **`queryUser2` 结束时**,`queryUserLoading.value` 为 `false`
|
|
222
|
+
|
|
223
|
+
对于 `useAsyncData`,`queryUser` 对应的 `user` 数据:
|
|
224
|
+
|
|
225
|
+
- `user.value` 将始终以 `queryUser2` 的结果为准(取**最晚的调用**产生的结果)
|
|
226
|
+
- 即使 `queryUser1` 产生结果的时间更晚,但 `queryUser1` 的结果不会更新到 `user.value` 上
|
|
227
|
+
- `queryUser1` 的结果会被自动舍弃,因为这是一个“过期的”数据
|
|
228
|
+
|
|
229
|
+
> 另一种处理重复调用的方式是不允许在上次调用未结束时再次调用
|
|
230
|
+
>
|
|
231
|
+
> 由于 `useAsync` 和 `useAsyncData` 提供了响应式的 `loading`,对于常用组件库,向按钮传入 `loading` 状态或在 `disabled` 属性中添加 `loading` 判断即可轻松避免
|
|
232
|
+
>
|
|
233
|
+
> 此处讨论的是因数据源变动导致异步数据需要刷新的场景,比如 `userId` 短时间从 `1` 切换到 `2`,`user` 数据需要做对应刷新
|
|
234
|
+
|
|
235
|
+
### 数据过期
|
|
236
|
+
|
|
237
|
+
> 过期数据标注是 `useAsyncData` 独有功能
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
const {
|
|
241
|
+
user,
|
|
242
|
+
queryUser,
|
|
243
|
+
queryUserError,
|
|
244
|
+
userExpired
|
|
245
|
+
} = useAsyncData('user', () => {})
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
假设是以下时间线:
|
|
249
|
+
|
|
250
|
+
- 首次调用 `queryUser`,记做 `queryUser1`
|
|
251
|
+
- `queryUser1` 未结束,再次调用 `queryUser`,记做 `queryUser2`
|
|
252
|
+
- `queryUser2` 先结束,**但发生报错**
|
|
253
|
+
- `queryUser1` 后结束
|
|
254
|
+
|
|
255
|
+
此时:
|
|
256
|
+
- `queryUser2` 的错误会被记录到 `queryUserError.value` 上
|
|
257
|
+
- `queryUser1` 的结果**会被更新到** `user.value` 上
|
|
258
|
+
- `userExpired.value` 会被设置为 `true`,表明当前的 `user.value` 是个过期数据(因为 `user.value` 来自`queryUser1`,而最新的结果是 `queryUser2` 的报错)
|
|
259
|
+
|
|
260
|
+
> 大多数情况下,无需考虑 `userExpired.value` 的问题,因为它只会在 `queryUserError.value` 出现时出现,优先处理 `queryUserError.value` 往往是更好的做法
|
|
261
|
+
|
|
262
|
+
> `userExpired.value` 为 `true` 后,直到下次调用更新 `user.value` 前,`userExpired.value` 将一直保持 `true` 状态。
|
|
263
|
+
> 原因:`userExpired.value` 为 `true` 后,虽然再次调用了 `queryUser`,但在 `user.value` 再次更新前,`user.value` 当前的值仍是“过期的”珊瑚橘
|
|
264
|
+
|
|
265
|
+
> 由于乱序的问题,如果在 `queryUser1` 时使用了 `.then(res => ...)`,`res` 的值是 `queryUser1` 的结果,与 `user.value` 不一致,这是符合预期的。
|
|
266
|
+
|
|
267
|
+
### 异步数据的中途获取与更新
|
|
268
|
+
|
|
269
|
+
> 异步数据中途获取与更新是 `useAsyncData` 独有功能
|
|
270
|
+
|
|
271
|
+
在某些场景下,我们希望异步数据可以在异步获取过程中更新,无需等到获取过程完全结束。
|
|
272
|
+
|
|
273
|
+
可以通过 `options.enhanceFirstArgument = true` 支持这种需求。
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
import { unFirstArgumentEnhanced, useAsyncData } from 'vue-asyncx'
|
|
277
|
+
|
|
278
|
+
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
279
|
+
|
|
280
|
+
const {
|
|
281
|
+
progress,
|
|
282
|
+
queryProgress
|
|
283
|
+
} = useAsyncData('progress', async (init?: number) => {
|
|
284
|
+
const { getData, updateData } = unFirstArgumentEnhanced(init)
|
|
285
|
+
init = unFirstArgumentEnhanced(init).firstArgument
|
|
286
|
+
updateData(init || 0)
|
|
287
|
+
await wait(100)
|
|
288
|
+
updateData(50)
|
|
289
|
+
await wait(100)
|
|
290
|
+
return 100
|
|
291
|
+
}, {
|
|
292
|
+
enhanceFirstArgument: true
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
queryProgress(10)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
在上述代码的最后一行调用 `queryProgress(10)` 后:
|
|
299
|
+
- `progress.value` 会立即更新为 `10`
|
|
300
|
+
- 100ms 后 `progress.value` 更新为 `50`
|
|
301
|
+
- 再过 100ms 后,`progress.value` 更新为 `100`,本次调用结束
|
|
302
|
+
|
|
303
|
+
注意到在传入 `useAsyncData` 的 `fn` 内,有
|
|
304
|
+
|
|
305
|
+
```js
|
|
306
|
+
const { getData, updateData } = unFirstArgumentEnhanced(init)
|
|
307
|
+
init = unFirstArgumentEnhanced(init).firstArgument
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
原理:
|
|
311
|
+
|
|
312
|
+
当 `options.enhanceFirstArgument = true` 后
|
|
313
|
+
- `fn` 的首个参数 `init` 便被拓展为 `FirstArgumentEnhanced` 类型。
|
|
314
|
+
- 实际调用 `queryProgress(10)` 时传入的 `10` 在 `fn` 内部通过 `init` 接收时变成了 `{ getData, updateData, firstArgument }`,`10` 被赋在了 `init.firstArgument` 上
|
|
315
|
+
- `unFirstArgumentEnhanced(init)` 先解构出 `getData` 和 `updateData`,再将 `init` 重新赋值为传入的 `10`
|
|
316
|
+
|
|
317
|
+
> 也就是在 `fn` 内部可以直接通过
|
|
318
|
+
> `const { getData, updateData, firstArgument } = init`
|
|
319
|
+
> 但是这样写需要自行处理 TS 类型问题,因此提供了 `unFirstArgumentEnhanced` 辅助函数。
|
|
320
|
+
|
|
321
|
+
只需要记住
|
|
322
|
+
|
|
323
|
+
- 可以像以往一般正常定义 `fn`、调用 `queryProgress`
|
|
324
|
+
- 在 `fn` 内部实现的最上方,加上上述的两行代码,余下即可正常使用
|
|
325
|
+
|
|
326
|
+
如果 `fn` 本身不接收参数,那么可以改成普通函数,通过 `arguments[0]` 传递:
|
|
327
|
+
|
|
328
|
+
```js
|
|
329
|
+
const {
|
|
330
|
+
progress,
|
|
331
|
+
queryProgress
|
|
332
|
+
} = useAsyncData('progress', async function () {
|
|
333
|
+
const { getData, updateData } = unFirstArgumentEnhanced(arguments[0])
|
|
334
|
+
updateData(0)
|
|
335
|
+
await wait(100)
|
|
336
|
+
updateData(50)
|
|
337
|
+
await wait(100)
|
|
338
|
+
return 100
|
|
339
|
+
}, {
|
|
340
|
+
enhanceFirstArgument: true
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
queryProgress()
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
在某些情况下,需要区分 `queryProgress()` 与 `queryProgress(undefined)`,可以通过:
|
|
347
|
+
|
|
348
|
+
```js
|
|
349
|
+
const firstArgumentEnhanced = unFirstArgumentEnhanced(arguments[0])
|
|
350
|
+
const undefinedOrEmpty = 'firstArgument' in firstArgumentEnhanced
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
即
|
|
354
|
+
|
|
355
|
+
- `firstArgumentEnhanced` 存在 `firstArgument` 属性时,是 `queryProgress(undefined)`
|
|
356
|
+
- `firstArgumentEnhanced` 不存在 `firstArgument` 属性时,是 `queryProgress()`
|
|
357
|
+
|
|
358
|
+
本块 api 设计思考过程见
|
|
359
|
+
|
|
360
|
+
- [rfc1-async-data-update](./docs/rfc1-async-data-update.md)
|
|
361
|
+
|
|
362
|
+
其它
|
|
363
|
+
|
|
364
|
+
- `getData` 总是返回最新的数据
|
|
365
|
+
- `setData` 不保证设置成功(比如发生了乱序响应的情况)
|
|
366
|
+
- 接上,`setData` 支持处理乱序响应,一旦有新的调用触发 `setData`,旧调用的 `setData` 操作会被忽略
|
|
367
|
+
|
|
368
|
+
## 理念
|
|
369
|
+
|
|
370
|
+
在“干净架构”里,函数拆分的原则是:一个函数应该做且只做一件事
|
|
371
|
+
|
|
372
|
+
- 对于传入 `useAsyncData` 的函数 `fn` 而言,唯一需要关注的就是如何设置 `data` 的值
|
|
373
|
+
- 对于在 `data` 值初始化后需要干的其它事情,可以在 `useAsyncData` **外部** 通过 vue 的 watch data 去触发
|
|
374
|
+
- 对于 `useAsyncData` 整体而言,我们将与 `data` 值相关的代码都聚集在了一起
|
|
375
|
+
- loading、error、expired、arguments 等数据与 data 或 fn 紧密相关
|
|
376
|
+
- watch 设置了 fn 的调用时机,即初始化 data 的时机
|
|
377
|
+
|
|
378
|
+
通过上面的约定,很容易写出“干净”的函数与高内聚的代码块,提高代码可读性
|
|
379
|
+
|
package/dist/vue-asyncx.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { WatchSource } from 'vue';
|
|
|
5
5
|
|
|
6
6
|
declare type FirstArgumentEnhanced<T = any, D = any> = {
|
|
7
7
|
[FLAG_FIRST_ARGUMENT_ENHANCED]: true;
|
|
8
|
-
firstArgument
|
|
8
|
+
firstArgument?: T;
|
|
9
9
|
getData: () => D;
|
|
10
10
|
updateData: (v: D) => void;
|
|
11
11
|
};
|
|
@@ -14,7 +14,7 @@ declare const FLAG_FIRST_ARGUMENT_ENHANCED = "__va_fae";
|
|
|
14
14
|
|
|
15
15
|
declare type StringDefaultWhenEmpty<S extends string, D extends string> = S extends '' ? D : S;
|
|
16
16
|
|
|
17
|
-
export declare function unFirstArgumentEnhanced<Arg = any, Data = any>(arg: Arg): FirstArgumentEnhanced<Arg, Data
|
|
17
|
+
export declare function unFirstArgumentEnhanced<Arg = any, Data = any>(arg: Arg): Arg extends undefined ? FirstArgumentEnhanced<Arg, Data> : Required<FirstArgumentEnhanced<Arg, Data>>;
|
|
18
18
|
|
|
19
19
|
declare function useAsync<Fn extends (...args: any) => any>(fn: Fn, options?: UseAsyncOptions<Fn>): UseAsyncResult<Fn, 'method'>;
|
|
20
20
|
|
package/dist/vue-asyncx.js
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
var L = Object.defineProperty, O = Object.defineProperties;
|
|
2
2
|
var b = Object.getOwnPropertyDescriptors;
|
|
3
3
|
var F = Object.getOwnPropertySymbols;
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
for (var
|
|
7
|
-
|
|
4
|
+
var B = Object.prototype.hasOwnProperty, $ = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var T = (e, n, r) => n in e ? L(e, n, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[n] = r, w = (e, n) => {
|
|
6
|
+
for (var r in n || (n = {}))
|
|
7
|
+
B.call(n, r) && T(e, r, n[r]);
|
|
8
8
|
if (F)
|
|
9
|
-
for (var
|
|
10
|
-
|
|
9
|
+
for (var r of F(n))
|
|
10
|
+
$.call(n, r) && T(e, r, n[r]);
|
|
11
11
|
return e;
|
|
12
|
-
},
|
|
13
|
-
var
|
|
14
|
-
var
|
|
12
|
+
}, C = (e, n) => O(e, b(n));
|
|
13
|
+
var U = (e, n) => {
|
|
14
|
+
var r = {};
|
|
15
15
|
for (var a in e)
|
|
16
|
-
|
|
16
|
+
B.call(e, a) && n.indexOf(a) < 0 && (r[a] = e[a]);
|
|
17
17
|
if (e != null && F)
|
|
18
18
|
for (var a of F(e))
|
|
19
|
-
n.indexOf(a) < 0 &&
|
|
20
|
-
return
|
|
19
|
+
n.indexOf(a) < 0 && $.call(e, a) && (r[a] = e[a]);
|
|
20
|
+
return r;
|
|
21
21
|
};
|
|
22
22
|
import { ref as E, watch as x, computed as N } from "vue";
|
|
23
23
|
function j(...e) {
|
|
24
24
|
var v;
|
|
25
25
|
if (!Array.isArray(e) || !e.length) throw TypeError("参数错误:未传递");
|
|
26
|
-
const { name: n, fn:
|
|
26
|
+
const { name: n, fn: r, options: a } = typeof e[0] == "function" ? { name: "method", fn: e[0], options: e[1] } : { name: e[0] || "method", fn: e[1], options: e[2] };
|
|
27
27
|
if (typeof n != "string") throw TypeError("参数错误:name");
|
|
28
|
-
if (typeof
|
|
28
|
+
if (typeof r != "function") throw TypeError("参数错误:fn");
|
|
29
29
|
const p = E(!1), m = E(), s = E(), u = { called: 0, finished: 0 };
|
|
30
30
|
function y(...h) {
|
|
31
|
-
const d = ++u.called, A = (
|
|
32
|
-
s.value = void 0, p.value = !0, m.value =
|
|
33
|
-
}, c = (
|
|
34
|
-
o > u.finished && (u.finished = o), u.called === u.finished && (i === "error" && (s.value =
|
|
31
|
+
const d = ++u.called, A = (t) => {
|
|
32
|
+
s.value = void 0, p.value = !0, m.value = t;
|
|
33
|
+
}, c = (t, { scene: i, sn: o }) => {
|
|
34
|
+
o > u.finished && (u.finished = o), u.called === u.finished && (i === "error" && (s.value = t), p.value = !1, m.value = void 0);
|
|
35
35
|
};
|
|
36
36
|
A(h);
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
39
|
-
return
|
|
38
|
+
const t = r(...h);
|
|
39
|
+
return t instanceof Promise ? t.then(
|
|
40
40
|
() => c(void 0, { scene: "normal", sn: d }),
|
|
41
41
|
(i) => c(i, { scene: "error", sn: d })
|
|
42
|
-
) : c(void 0, { scene: "normal", sn: d }),
|
|
43
|
-
} catch (
|
|
44
|
-
throw c(
|
|
42
|
+
) : c(void 0, { scene: "normal", sn: d }), t;
|
|
43
|
+
} catch (t) {
|
|
44
|
+
throw c(t, { scene: "error", sn: d }), t;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
if (a) {
|
|
@@ -50,7 +50,7 @@ function j(...e) {
|
|
|
50
50
|
{},
|
|
51
51
|
"immediate" in a ? { immediate: a.immediate } : {},
|
|
52
52
|
(v = a.watchOptions) != null ? v : {}
|
|
53
|
-
), { handlerCreator: d } = l, A =
|
|
53
|
+
), { handlerCreator: d } = l, A = U(l, ["handlerCreator"]), { watch: c } = a, i = (() => {
|
|
54
54
|
const o = () => y();
|
|
55
55
|
if (typeof d != "function") return o;
|
|
56
56
|
try {
|
|
@@ -71,16 +71,16 @@ function j(...e) {
|
|
|
71
71
|
}
|
|
72
72
|
function G(e) {
|
|
73
73
|
if (!e) return "";
|
|
74
|
-
const n = e[0],
|
|
75
|
-
return n.toUpperCase() +
|
|
74
|
+
const n = e[0], r = e.slice(1);
|
|
75
|
+
return n.toUpperCase() + r;
|
|
76
76
|
}
|
|
77
77
|
const D = "__va_fae";
|
|
78
78
|
function z(...e) {
|
|
79
79
|
if (!Array.isArray(e) || !e.length) throw TypeError("参数错误:未传递");
|
|
80
|
-
const { name: n, fn:
|
|
80
|
+
const { name: n, fn: r, options: a } = typeof e[0] == "function" ? { name: "data", fn: e[0], options: e[1] } : { name: e[0] || "data", fn: e[1], options: e[2] };
|
|
81
81
|
if (typeof n != "string") throw TypeError("参数错误:name");
|
|
82
|
-
if (typeof
|
|
83
|
-
const c = a || {}, { enhanceFirstArgument: p } = c, m =
|
|
82
|
+
if (typeof r != "function") throw TypeError("参数错误:fn");
|
|
83
|
+
const c = a || {}, { enhanceFirstArgument: p } = c, m = U(c, ["enhanceFirstArgument"]), s = E({
|
|
84
84
|
// 调用序号(即:fn 第 called 次调用)
|
|
85
85
|
called: 0,
|
|
86
86
|
// 完成序号(即:fn 第 finished 次调用完成)
|
|
@@ -90,30 +90,30 @@ function z(...e) {
|
|
|
90
90
|
// 数据完成更新序号(即:data 数据由第 dataUpdateByFinished 次调用完成后更新)
|
|
91
91
|
dataUpdateByFinished: 0
|
|
92
92
|
}), u = E(), y = N(() => !(s.value.dataUpdateByFinished >= s.value.finished || s.value.dataUpdateByCalled > s.value.finished));
|
|
93
|
-
function v(
|
|
94
|
-
i < s.value.dataUpdateByCalled || (u.value =
|
|
93
|
+
function v(t, { sn: i, scene: o }) {
|
|
94
|
+
i < s.value.dataUpdateByCalled || (u.value = t, s.value.dataUpdateByCalled = i, o === "finish" && (s.value.dataUpdateByFinished = i));
|
|
95
95
|
}
|
|
96
|
-
function l(
|
|
97
|
-
i === "normal" && v(
|
|
96
|
+
function l(t, { scene: i, sn: o }) {
|
|
97
|
+
i === "normal" && v(t, { sn: o, scene: "finish" }), o > s.value.finished && (s.value.finished = o);
|
|
98
98
|
}
|
|
99
|
-
function h(
|
|
99
|
+
function h(t, {
|
|
100
100
|
enhanceFirstArgument: i,
|
|
101
101
|
sn: o
|
|
102
102
|
}) {
|
|
103
|
-
if (!i) return
|
|
104
|
-
const [f, ...H] =
|
|
105
|
-
return [{
|
|
106
|
-
[D]: !0
|
|
107
|
-
|
|
103
|
+
if (!i) return t;
|
|
104
|
+
const [f, ...H] = t;
|
|
105
|
+
return [C(w({
|
|
106
|
+
[D]: !0
|
|
107
|
+
}, t.length ? { firstArgument: f } : {}), {
|
|
108
108
|
getData: () => u.value,
|
|
109
|
-
updateData: (
|
|
110
|
-
}, ...H];
|
|
109
|
+
updateData: (_) => (v(_, { sn: o, scene: "update" }), _)
|
|
110
|
+
}), ...H];
|
|
111
111
|
}
|
|
112
|
-
function d(...
|
|
112
|
+
function d(...t) {
|
|
113
113
|
const i = ++s.value.called;
|
|
114
|
-
|
|
114
|
+
t = h(t, { enhanceFirstArgument: p, sn: i });
|
|
115
115
|
try {
|
|
116
|
-
const o =
|
|
116
|
+
const o = r(...t);
|
|
117
117
|
return o instanceof Promise ? o.then(
|
|
118
118
|
// promise 正常结束
|
|
119
119
|
(f) => l(f, { scene: "normal", sn: i }),
|
|
@@ -125,7 +125,7 @@ function z(...e) {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
const A = j(`query${G(n)}`, d, m);
|
|
128
|
-
return
|
|
128
|
+
return C(w({}, A), {
|
|
129
129
|
[n]: u,
|
|
130
130
|
[`${n}Expired`]: y
|
|
131
131
|
});
|
package/dist/vue-asyncx.umd.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,n){typeof exports=="object"&&typeof module!="undefined"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(e=typeof globalThis!="undefined"?globalThis:e||self,n(e.VueAsyncx={},e.Vue))})(this,function(e,n){"use strict";var P=Object.defineProperty,q=Object.defineProperties;var x=Object.getOwnPropertyDescriptors;var C=Object.getOwnPropertySymbols;var
|
|
1
|
+
(function(e,n){typeof exports=="object"&&typeof module!="undefined"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(e=typeof globalThis!="undefined"?globalThis:e||self,n(e.VueAsyncx={},e.Vue))})(this,function(e,n){"use strict";var P=Object.defineProperty,q=Object.defineProperties;var x=Object.getOwnPropertyDescriptors;var C=Object.getOwnPropertySymbols;var $=Object.prototype.hasOwnProperty,j=Object.prototype.propertyIsEnumerable;var O=(e,n,a)=>n in e?P(e,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[n]=a,U=(e,n)=>{for(var a in n||(n={}))$.call(n,a)&&O(e,a,n[a]);if(C)for(var a of C(n))j.call(n,a)&&O(e,a,n[a]);return e},_=(e,n)=>q(e,x(n));var B=(e,n)=>{var a={};for(var c in e)$.call(e,c)&&n.indexOf(c)<0&&(a[c]=e[c]);if(e!=null&&C)for(var c of C(e))n.indexOf(c)<0&&j.call(e,c)&&(a[c]=e[c]);return a};function a(...t){var w;if(!Array.isArray(t)||!t.length)throw TypeError("参数错误:未传递");const{name:f,fn:p,options:y}=typeof t[0]=="function"?{name:"method",fn:t[0],options:t[1]}:{name:t[0]||"method",fn:t[1],options:t[2]};if(typeof f!="string")throw TypeError("参数错误:name");if(typeof p!="function")throw TypeError("参数错误:fn");const v=n.ref(!1),E=n.ref(),s=n.ref(),l={called:0,finished:0};function F(...A){const h=++l.called,T=i=>{s.value=void 0,v.value=!0,E.value=i},u=(i,{scene:o,sn:r})=>{r>l.finished&&(l.finished=r),l.called===l.finished&&(o==="error"&&(s.value=i),v.value=!1,E.value=void 0)};T(A);try{const i=p(...A);return i instanceof Promise?i.then(()=>u(void 0,{scene:"normal",sn:h}),o=>u(o,{scene:"error",sn:h})):u(void 0,{scene:"normal",sn:h}),i}catch(i){throw u(i,{scene:"error",sn:h}),i}}if(y){const A=()=>{},m=Object.assign({},"immediate"in y?{immediate:y.immediate}:{},(w=y.watchOptions)!=null?w:{}),{handlerCreator:h}=m,T=B(m,["handlerCreator"]),{watch:u}=y,o=(()=>{const r=()=>F();if(typeof h!="function")return r;try{const d=h(F);return typeof d=="function"?d:r}catch(d){return r}})();n.watch(u!=null?u:A,o,T)}return{[f]:F,[`${f}Loading`]:v,[`${f}Arguments`]:E,[`${f}Error`]:s}}function c(t){if(!t)return"";const f=t[0],p=t.slice(1);return f.toUpperCase()+p}const b="__va_fae";function H(...t){if(!Array.isArray(t)||!t.length)throw TypeError("参数错误:未传递");const{name:f,fn:p,options:y}=typeof t[0]=="function"?{name:"data",fn:t[0],options:t[1]}:{name:t[0]||"data",fn:t[1],options:t[2]};if(typeof f!="string")throw TypeError("参数错误:name");if(typeof p!="function")throw TypeError("参数错误:fn");const u=y||{},{enhanceFirstArgument:v}=u,E=B(u,["enhanceFirstArgument"]),s=n.ref({called:0,finished:0,dataUpdateByCalled:0,dataUpdateByFinished:0}),l=n.ref(),F=n.computed(()=>!(s.value.dataUpdateByFinished>=s.value.finished||s.value.dataUpdateByCalled>s.value.finished));function w(i,{sn:o,scene:r}){o<s.value.dataUpdateByCalled||(l.value=i,s.value.dataUpdateByCalled=o,r==="finish"&&(s.value.dataUpdateByFinished=o))}function m(i,{scene:o,sn:r}){o==="normal"&&w(i,{sn:r,scene:"finish"}),r>s.value.finished&&(s.value.finished=r)}function A(i,{enhanceFirstArgument:o,sn:r}){if(!o)return i;const[d,...N]=i;return[_(U({[b]:!0},i.length?{firstArgument:d}:{}),{getData:()=>l.value,updateData:D=>(w(D,{sn:r,scene:"update"}),D)}),...N]}function h(...i){const o=++s.value.called;i=A(i,{enhanceFirstArgument:v,sn:o});try{const r=p(...i);return r instanceof Promise?r.then(d=>m(d,{scene:"normal",sn:o}),d=>m(d,{scene:"error",sn:o})):m(r,{scene:"normal",sn:o}),r}catch(r){throw m(r,{scene:"error",sn:o}),r}}const T=a(`query${c(f)}`,h,E);return _(U({},T),{[f]:l,[`${f}Expired`]:F})}function L(t){if(typeof t!="object"||!t||!t[b])throw Error("请配置 options.enhanceFirstArgument = true");return t}e.unFirstArgumentEnhanced=L,e.useAsync=a,e.useAsyncData=H,e.useAsyncFunction=a,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue-asyncx",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/vue-asyncx.d.ts",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"build": "vite build",
|
|
45
45
|
"test:unit": "vitest",
|
|
46
46
|
"test:unit:coverage": "vitest run --coverage",
|
|
47
|
-
"test": "pnpm run test:unit"
|
|
47
|
+
"test": "pnpm run test:unit",
|
|
48
|
+
"prepublish": "pnpm run build"
|
|
48
49
|
}
|
|
49
50
|
}
|