在以往的业务项目中,我们经常会被 loading 状态的管理等重复的功能实现所困扰。每次开启一个新项目都需要重新实现一遍,这是一项重复的工作。
VueRequest 的目的是为开发人员提供一种方便、快速的方式来管理 API 状态。通过简单的配置,可以省去那些繁琐的任务,专注于业务核心的开发。
以下是我自己简单实现的,当练练手了。
使用 - 一开始就请求数据
js
const request_apiStatisticsFormat = () => apiStatisticsFormat({ schoolId, groupCode, groupType });
const { data, loading, error, run } = useRequest(request_apiStatisticsFormat, {
ready: true,
deps: [],
isShowLoading: true,
onSuccess: (res) => {
console.log('res', res);
studentList.value = res.data.studentList;
},
});
api的相关方法
ts
export const apiGetStudentList = (params: IParams) =>
get<ResponseData>(`/k12-app-api/api/npad/homeSchool/interact/getStudentList`, {
params,
// baseURL: 'https://ntteacher-api-test1.test.xdf.cn',
});
export const apiStatisticsFormat = (params: IParams) => formatApiFn(apiGetStudentList, params, mockData);
export interface ResponseData<T = any> {
data: T;
status?: string | number;
code?: string | number;
msg: string;
success: boolean;
}
export const formatApiFn = async (
apiFn: (params?: any) => Promise<any>,
params: any,
mockData?: any,
) => {
try {
const sleep = (time: number) => new Promise((resolve) => setTimeout(resolve, time));
mockData && (await sleep(1000));
const res = mockData ? mockData : await apiFn(params);
const { data } = res;
if (data.success) return data;
return Promise.reject(data);
} catch (error) {
return Promise.reject(error);
}
};
使用 - 等特定条件才开始请求数据
ts
const { data, loading, error, run } = useRequest(request_apiStatisticsFormat, {
// ready取决于isSelectedDate
ready: isSelectedDate.value,
// date值一旦发生变化,就重新请求数据
deps: [date],
isShowLoading: true,
onSuccess: (res) => {
console.log('res', res);
const {
connectStudentList: connectStudentListData,
notConnectStudentList: notConnectStudentListData,
statistics: statisticsData,
} = res.data || {};
},
});
useRequest
ts
import { ref, watch } from 'vue';
import { Toast } from 'vant';
import Vue from 'vue';
Vue.use(Toast);
interface IUseRequestOptions {
// 请求成功回调
onSuccess?: (data: any) => void;
// 请求错误回调
onError?: (error: any) => void;
// 请求结束回调
onFinally?: () => void;
// 是否自动执行,默认是true
ready?: boolean;
// 监听依赖项,自动执行
deps?: any[];
// 初始data值
initialData?: any;
// 是否展示加载效果
isShowLoading?: boolean;
}
export const useRequest = (
requestFn: (requestParams?: any) => Promise<any>,
options?: IUseRequestOptions,
) => {
// 初始值
const data = ref(options?.initialData);
// loading 是否正在请求
const loading = ref(false);
// error 是否请求错误
const error = ref(null);
const { onSuccess, onError, onFinally, ready, deps, isShowLoading } = options || {
ready: true,
};
// run就是请求函数
const run = async () => {
error.value = null;
// 正在请求中
loading.value = true;
console.log('开始', 1);
try {
// 请求函数
const res = await requestFn();
// 请求结果赋值
data.value = res;
console.log('成功', res);
error.value = null;
// 请求成功回调
onSuccess && onSuccess(res);
} catch (err) {
console.log('错误', err);
// 请求错误赋值
error.value = err;
// 请求错误回调
onError && onError(err);
} finally {
// 请求结束
loading.value = false;
// 请求结束回调
onFinally && onFinally();
}
};
// 是否展示加载效果
isShowLoading &&
watch(loading, (newVal) => {
newVal
? Toast.loading({
message: '加载中...',
forbidClick: true,
})
: Toast.clear();
});
// 如果ready为true,自动执行
ready && run();
// 监听依赖项,自动执行
watch(deps, () => {
run();
});
return {
data,
loading,
error,
run,
};
};
错误状态显示
vue
<!-- 使用
https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/error_3.png
https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/empty3.png
<StatusPage v-if="error" text="网络错误,请稍候再试~" :showLoadBtn="true" @reload="run" :isFullScreen="true"/>
<StatusPage v-if="isEmpty" text="暂无数据~" :isFullScreen="true" />
局部空数据展示
<div v-if="isEmpty" style="height: 300px; position: relative" >
<StatusPage :isFullScreen="false" text="暂无数据~" />
</div>
-->
<template>
<div class="status-page-box" :class="{isFullScreen}">
<div class="empty-error-page" id="pageError">
<img class="img" src="@/assets/images/error.png" />
<div class="des">{{ text }}</div>
<div v-if="showLoadBtn" class="btn-load" @click="$emit('reload')">重新加载</div>
</div>
</div>
</template>
<script setup lang="ts">
import { defineProps } from 'vue';
const props = defineProps({
text: {
type: String,
},
isFullScreen: {
type: Boolean,
default: true,
},
showLoadBtn: {
type: Boolean,
default: false,
},
});
</script>
<style scoped lang="less">
.isFullScreen{
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: #fafafa;
z-index: 888888;
}
.empty-error-page {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 8;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
text-align: center;
.img {
width: 102px;
height: 97px;
}
.des {
font-size: 14px;
color: #999;
}
.btn-load {
font-size: 14px;
font-weight: 500;
color: #ffffff;
line-height: 34px;
width: 100px;
height: 34px;
background: linear-gradient(180deg, #49d391 0%, #34d0aa 100%);
border-radius: 17px;
text-align: center;
margin-top: 20px;
}
}
</style>