useAsyncState
Reactive async state. Will not block your setup function and will trigger changes once the promise is ready. 反应式异步状态。不会阻止您的设置功能,并且会在承诺准备就绪后触发更改
刚在用真香,又可以多些时间愉快地摸鱼,稍稍看一下其中实现如下:
// hook/useAsyncState/index.ts
// import { noop, promiseTimeout } from '@vueuse/shared'
// import type { Ref, UnwrapRef } from 'vue'
// import { ref, shallowRef } from 'vue'
function noop(): void {}
function promiseTimeout(ms: number, throwOnTimeout?: boolean, reason?: string) :Promise<void>{
return new Promise((resolve, reject) => {
setTimeout(() => {
throwOnTimeout ? reject(reason) : resolve(undefined)
}, ms)
})
}
export interface UseAsyncStateReturn<Data, Shallow extends boolean> {
state: Shallow extends true ? Ref<Data> : Ref<UnwrapRef<Data>>
isReady: Ref<boolean>
isLoading: Ref<boolean>
error: Ref<unknown>
execute: (delay: number, ...args: any[]) => Promise<Data>
}
/**
* options入参类型
* @param delay 首次进入延时执行函数时间
* @param immediate 是否立即执行
* @param onError 错误回调函数
* @param resetOnExecute 函数执行前把state重置成initialState
* @param shallow 是否shallowRef
*/
export interface AsyncStateOptions<Shallow extends boolean> {
delay?: number
immediate?: boolean
onError?: (e: unknown) => void
resetOnExecute?: boolean
shallow?: Shallow
}
export function useAsyncState<Data, Shallow extends boolean = true>(
promise: Promise<Data> | ((...args: any[]) => Promise<Data>),
initialState: Data,
options?: AsyncStateOptions<Shallow>,
): UseAsyncStateReturn<Data, Shallow> {
const {
immediate = true,
delay = 0,
onError = noop,
resetOnExecute = true,
shallow = true,
} = options ?? {}
const state = shallow ? shallowRef(initialState) : ref(initialState)
const isReady = ref(false)
const isLoading = ref(false)
const error = ref<unknown | undefined>(undefined)
// 包装异步函数进行变更状态赋值结果
async function execute(delay: number = 0, ...args: any[]) {
if (resetOnExecute)
state.value = initialState
error.value = undefined
isReady.value = false
isLoading.value = true
if (delay > 0)
await promiseTimeout(delay)
const _promise = typeof promise === 'function'
? promise(...args)
: promise
try {
state.value = await _promise
isReady.value = true
} catch (err) {
error.value = err
onError(err)
} finally {
isLoading.value = false
}
return state.value as Data
}
// 首次是否延迟delay毫秒执行包装函数
if (immediate)
execute(delay)
// 返回响应式数据
return {
state: state as Shallow extends true ? Ref<Data> : Ref<UnwrapRef<Data>>,
isReady,
isLoading,
error,
execute,
}
}
demo
// useAsyncStateDemo.vue
<script setup lang="ts">
import axios from "axios";
import { useAsyncState } from "../../hook/useAsyncState/index.ts";
function request(args) {
const id = args?.id || 1;
return axios
.get(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then((t) => t.data);
}
const { isLoading, state, isReady, execute } = useAsyncState(
request,
{ a: 1 },
{
delay: 0,
resetOnExecute: false,
}
);
watch(
state,
(x) => {
console.log("state=>", x);
},
{
immediate: true,
}
);
</script>
<template>
<div>
<p>Ready: {{ isReady.toString() }}</p>
<p>Loading: {{ isLoading.toString() }}</p>
<pre lang="json" class="ml-2">{{ state }}</pre>
<button
m-2
px-2
py-1
bg-gray-500
color-white
border="~ base rounded"
@click="execute(2000, { id: 3 })"
>
Execute
</button>
</div>
</template>
最后
~~useAsyncState实现过程比较简单,但是参数较多,看一下实现过程理解更深 ~~ 最后分享一下unplugin-auto-import和unplugin-vue-components这两个vite插件自动地帮我们处理依赖引入和组件引入