本文举出两个hook使用的示例,方便理解hook的使用。选用ant-design-vue UI框架作为演示。
封装下拉框hook
我们在获取下拉框字典前会有个请求过程,那现在将请求过程封装入hook中。我们先分析下所需实现的代码:发送请求的方法、获得下拉字典及控制请求状态的变量、呈现组件。
下面是发送请求的代码:
// api.ts
export const getRemoteData = ()=>{
return new Promise<any[]>((resolve, reject)=>{
setTimeout(()=>{
if(Math.random() > 0.2){
resolve([{key:1,label:'苹果',value:1},{key:2,label:'香蕉',value:2},{key:3,label:'橘子',value:3}]);
}else{
reject(new Error('意外错误'));
}
}, 1000);
});
}
下面是获得下拉字典及控制请求状态的变量的hook代码:
// hook.ts
import { onMounted, reactive, ref } from 'vue';
// 定义下拉框接受的数据格式
interface SelectOption {
value: string;
label: string;
disabled?: boolean;
key?: string;
}
// 定义hook方法入参类型
interface FetchSelectProps {
apiFun:()=>Promise<any[]>;
}
export default function useFetchSelect(props:FetchSelectProps){
const { apiFun } = props;
const options = ref<SelectOption[]>([]);
const loading = ref(false);
// 包装发送请求函数
const loadData = ()=>{
loading.value = true;
options.value = [];
return apiFun().then(res=>{
loading.value = false;
options.value = res;
return res;
}, (err)=>{
loading.value = false;
options.value = [{
value: -1,
label: err.message,
disabled: true,
}]
return Promise.reject(err);
});
}
onMounted(loadData);
return reactive({options,loading});
}
呈现组件代码:
// index.vue
<script setup lang="ts">
import useFetchSelect from './hook';
import { getRemoteData } from './api';
const selectBind = useFetchSelect({apiFun:getRemoteData}); // 如果不传入多个参数,可以用基本变量
</script>
<template>
<a-select :="selectBind"></a-select>
</template>
封装按钮加载状态hook
现在的按钮都需要个变量来展示加载状态,给予用户更好的体验,同时可以防止用户多次点击,虽然只是个增加一个变量状态,但是写多了也烦。实现功能所需的代码同上。
下面是发送请求的代码:
// api.ts
function submitApi(text:string){
return new Promise((resolve,reject) => {
setTimeout(() => {
if(Math.random()>0.5){
resolve({
status:'ok',
text:text,
})
}else{
reject(new Error('意外错误'));
}
},1000)
})
}
下面是包装发送请求的hook代码:
// hook.ts
import { ref } from 'vue';
import type { Ref } from 'vue';
// 定义发送请求方法的类型
type TApiFun<TData, TParams extends Array<any>> = (...params: TParams)=>Promise<TData>;
interface AutoRequestOptions {
loading?:boolean; // 定义初始状态
onSuccess?:(data:any)=>void; // 定义接口调用后出发的提示弹框回调;
}
type AutoRequestResult<TData, TParams extends Array<any>> = [Ref<boolean>,TApiFun<TData,TParams>];
// 相当于在api方法前 加了层修饰器
export default function useAutoRequest<TData, TParams extends any[] = any[]>(fun:TApiFun<TData,TParams>, options?:AutoRequestOptions):AutoRequestResult<TData,TParams> {
const {loading = false, onSuccess} = options||{loading:false};
const requestLoading = ref(loading);
const run:TApiFun<TData, TParams> = (...params) => {
requestLoading.value = true;
return fun(...params).then((res) => {
onSuccess && onSuccess(res);
return res;
}).finally(() => {
requestLoading.value = false;
})
}
return [requestLoading,run]
}
下面是呈现组件:
// index.vue
<script setup lang="ts">
import useAutoRequest from './hook';
import { submitApi } from './api';
const [loading, submit]= useAutoRequest(submitApi); // 可选传成功后弹框方法
const onSubmit = ()=>{
submit('aaa').then(res=>{
console.log('res', res);
})
}
</script>
<template>
<a-button :loading="loading" @click="onSubmit">提交</a-select>
</template>