怎么封装一个好用的 Hook

100 阅读2分钟

1. 什么是 Hook

  • 定义:Hook 是一种特殊的函数,它封装了具有状态的逻辑,使得代码可以复用。在 Vue 和 React 等现代前端框架中,Hook 允许开发者在组件之间共享状态逻辑。
  • 来源:Hook 概念源自 React,Vue 3 引入了类似的组合式 API。
  • 区别:Hook 函数与普通函数的区别在于 Hook 可以包含状态(响应式变量)。

2. 在 Vue 中使用 Hook

  • 目的:提高代码的可维护性和复用性。
  • 自定义 Hook 示例
    // mouse.js
    import { ref, onMounted, onUnmounted } from 'vue';
    export function useMouse() {
        const x = ref(0);
        const y = ref(0);
        function update(event) {
            x.value = event.pageX;
            y.value = event.pageY;
        }
        onMounted(() => window.addEventListener('mousemove', update));
        onUnmounted(() => window.removeEventListener('mousemove', update));
        return { x, y };
    }
    
  • 使用社区提供的 Hook
    import { useMouse } from 'VueUse';
    

3. 封装自定义 Hook

  • 场景分析:封装用于表格数据获取和分页的 Hook。
  • 封装 useTable
    // useTable.ts
    import { ref, reactive } from 'vue';
    export function useTable(api) {
        const data = ref([]);
        const refresh = () => api().then(res => data.value = res);
        refresh();
        return [data, refresh];
    }
    
  • 封装 usePagination
    // usePagination.ts
    import { reactive } from 'vue';
    export function usePagination(cb, sizeOption = [10, 20, 50, 100, 200]) {
        const pagination = reactive({
            current: 1,
            total: 0,
            sizeOption,
            size: sizeOption[0],
            onPageChange(page, extraData) {
                pagination.current = page;
                return extraData ? cb(extraData) : cb();
            },
            onSizeChange(size, extraData) {
                pagination.current = 1;
                pagination.size = size;
                return extraData ? cb(extraData) : cb();
            },
            setTotal(total) {
                pagination.total = total;
            },
            reset() {
                pagination.current = 1;
                pagination.total = 0;
                pagination.size = pagination.sizeOption[0];
            },
        });
        return [pagination, pagination.onPageChange, pagination.onSizeChange, pagination.setTotal];
    }
    

4. 封装技巧

  • 返回值:自定义 Hook 可以返回数组或对象。
  • 异步处理:在 Hook 中使用 then 而不是 await 处理异步请求。

5. 支持不同接口字段

  • 参数处理:允许自定义 Hook 接受参数,以适应不同的 API 需求。
  • 示例
    export function useTable(api, params = {}, options = {}) {
        const [pagination, , , setTotal] = usePagination(() => refresh());
        const data = ref([]);
        const refresh = (extraData = {}) => {
            const requestData = {
                [options.path.page]: pagination.current,
                [options.path.size]: pagination.size,
                ...params,
                ...extraData,
            };
            return api(requestData).then((res) => {
                data.value = get(res, options.path.data, []);
                setTotal(get(res, options.path.total, 0));
            });
        };
        options.immediate && refresh();
        return [data, refresh, pagination];
    }
    

6. 总结

  • Hook 提供了一种强大的方式,用于在 Vue 和 React 等框架中封装和复用状态逻辑。
  • 自定义 Hook 可以帮助开发者编写更优雅、更可维护的代码。
  • 通过封装和参数化,Hook 可以适应各种不同的业务场景和 API 需求。