在 Vue 3 中,组合式 API(Composition API)为我们提供了更灵活的方式来组织代码。今天,我们将实现一个通用的 useMutate Hook,用于处理数据请求并更新组件中的数据状态。这个 Hook 将帮助我们封装数据获取、格式化、状态管理等逻辑,从而使得组件更简洁且易于复用。

背景

在前端开发中,数据的请求和展示是常见的功能。为了避免在每个组件中重复相似的代码,我们可以将数据请求的逻辑抽象成一个可复用的 Hook。useMutate Hook 的目标就是封装数据请求、处理无数据状态和格式化数据的逻辑,帮助我们简化代码并提高开发效率。

需求分析

我们需要实现以下功能:
1. 数据请求: 向服务器请求数据。
2. 数据格式化: 支持自定义的数据格式化函数。
3. 请求成功回调: 请求成功后触发回调,方便后续操作。
4. 无数据状态管理: 判断返回数据是否为空,并更新 noData 状态。

代码实现

下面是我们实现的 useMutate Hook:

import { onMounted, ref } from 'vue';
import { useLockFn } from './useLockFn';

interface IMutateOption<T> {
    /** 默认数据 */
    defaultData?: T;

    /** 额外传递参数 */
    params?: Record<string, any>;

    /** 初始是否发出请求 */
    initRequest?: boolean;

    /** 获取数据 */
    getData: (data: Record<string, any>) => Promise<{ data?: T }>;

    /** 数据格式化 */
    dataFormat?(data?: T): any;

    /** 请求成功后的回调 */
    successCallback?(data?: T): void;
}

export function useMutate<T>(config: IMutateOption<T>) {
    const { defaultData, params, initRequest = true } = config;
    const detail = ref<T | undefined>(defaultData);
    const noData = ref<boolean>();
    const isInit = ref(initRequest);

    const mutate = useLockFn(async () => {
        try {
            const { data } = await config.getData({ ...params });
            const newData = config.dataFormat ? config.dataFormat(data) : data;

            detail.value = newData;
            config?.successCallback?.(newData);

            /**暂无数据 */
            let blo = false;

            if (newData !== undefined) {
                if (newData instanceof Array) {
                    blo = newData.length === 0;
                } else if (newData instanceof Object) {
                    blo = Object.keys(newData).length === 0;
                }
            } else {
                blo = true;
            }

            noData.value = blo;
        } catch (error) {
            console.log(error);
            noData.value = true;
        }
    }, 500);

    onMounted(() => {
        if (!isInit.value) {
            isInit.value = true;
        } else {
            mutate();
        }
    });

    return { mutate, detail, noData };
}

使用示例

const { detail, mutate, noData } = useMutate({
    getData: () => api['/wechat/api/outWorkOrder/express/{id}_GET'](1),
});