本文舉出兩個(gè)hook使用的示例,方便理解hook的使用。選用ant-design-vue UI框架作為演示。
封裝下拉框hook
我們?cè)讷@取下拉框字典前會(huì)有個(gè)請(qǐng)求過(guò)程,那現(xiàn)在將請(qǐng)求過(guò)程封裝入hook中。我們先分析下所需實(shí)現(xiàn)的代碼:發(fā)送請(qǐng)求的方法、獲得下拉字典及控制請(qǐng)求狀態(tài)的變量、呈現(xiàn)組件。
下面是發(fā)送請(qǐng)求的代碼:
// api.ts
export const getRemoteData = ()=>{
return new Promise<any[]>((resolve, reject)=>{
setTimeout(()=>{
if(Math.random() > 0.2){
resolve([{key:1,label:'蘋(píng)果',value:1},{key:2,label:'香蕉',value:2},{key:3,label:'橘子',value:3}]);
}else{
reject(new Error('意外錯(cuò)誤'));
}
}, 1000);
});
}
下面是獲得下拉字典及控制請(qǐng)求狀態(tài)的變量的hook代碼:
// hook.ts
import { onMounted, reactive, ref } from 'vue';
// 定義下拉框接受的數(shù)據(jù)格式
interface SelectOption {
value: string;
label: string;
disabled?: boolean;
key?: string;
}
// 定義hook方法入?yún)㈩愋?interface FetchSelectProps {
apiFun:()=>Promise<any[]>;
}
export default function useFetchSelect(props:FetchSelectProps){
const { apiFun } = props;
const options = ref<SelectOption[]>([]);
const loading = ref(false);
// 包裝發(fā)送請(qǐng)求函數(shù)
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});
}
呈現(xiàn)組件代碼:
// index.vue
<script setup lang="ts">
import useFetchSelect from './hook';
import { getRemoteData } from './api';
const selectBind = useFetchSelect({apiFun:getRemoteData}); // 如果不傳入多個(gè)參數(shù),可以用基本變量
</script>
<template>
<a-select :="selectBind"></a-select>
</template>

字典獲取成功.gif

字典獲取失敗.gif
封裝按鈕加載狀態(tài)hook
現(xiàn)在的按鈕都需要個(gè)變量來(lái)展示加載狀態(tài),給予用戶更好的體驗(yàn),同時(shí)可以防止用戶多次點(diǎn)擊,雖然只是個(gè)增加一個(gè)變量狀態(tài),但是寫(xiě)多了也煩。實(shí)現(xiàn)功能所需的代碼同上。
下面是發(fā)送請(qǐng)求的代碼:
// 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('意外錯(cuò)誤'));
}
},1000)
})
}
下面是包裝發(fā)送請(qǐng)求的hook代碼:
// hook.ts
import { ref } from 'vue';
import type { Ref } from 'vue';
// 定義發(fā)送請(qǐng)求方法的類型
type TApiFun<TData, TParams extends Array<any>> = (...params: TParams)=>Promise<TData>;
interface AutoRequestOptions {
loading?:boolean; // 定義初始狀態(tài)
onSuccess?:(data:any)=>void; // 定義接口調(diào)用后出發(fā)的提示彈框回調(diào);
}
type AutoRequestResult<TData, TParams extends Array<any>> = [Ref<boolean>,TApiFun<TData,TParams>];
// 相當(dāng)于在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]
}
下面是呈現(xiàn)組件:
// 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>

按鈕加載狀態(tài).gif