如何在大型项目中组织和管理 Vue3 Hooks?

533 阅读2分钟

众所周知,Vue Hooks(通常指 Composition API 中的功能)是 Vue 3 引入的一种代码组织方式,用于更灵活地组合和复用逻辑。但是在项目中大量使用这种写法该如何更好的搭建结构呢?以下是可供参考实践的简单示例。

一、Hooks 组织原则

  1. 单一职责每个 Hook 应专注于完成单一功能,避免功能过重。

  2. 模块化将 Hooks 拆分为独立的模块,便于复用和维护。

  3. 类型安全使用 TypeScript 明确类型,避免隐式 any

  4. 分层管理根据功能或业务逻辑将 Hooks 分层管理,避免混乱。


二、项目目录结构

以下是一个适用于大型项目的目录结构示例:

src/
├── hooks/                  # Hooks 主目录
│   ├── core/               # 核心功能 Hooks(与业务无关)
│   │   ├── useFetch.ts
│   │   ├── useEventListener.ts
│   │   └── useLocalStorage.ts
│   ├── domain/             # 领域相关 Hooks(与业务逻辑绑定)
│   │   ├── useUser.ts
│   │   ├── useProduct.ts
│   │   └── useOrder.ts
│   ├── ui/                 # UI 相关 Hooks(与组件逻辑绑定)
│   │   ├── useForm.ts
│   │   ├── useModal.ts
│   │   └── usePagination.ts
│   ├── shared/             # 跨项目共享的 Hooks
│   │   ├── useAuth.ts
│   │   └── useConfig.ts
│   └── types/              # Hooks 类型定义
│       ├── hooks.d.ts
│       └── domain.d.ts
├── components/             # 组件目录
├── store/                  # 状态管理目录
├── utils/                  # 工具函数目录
└── services/               # API 服务目录

三、Hooks 分层管理

1. Core Hooks (核心层)

  • 功能:提供与业务无关的基础功能(如网络请求、事件监听、本地存储等)。

  • 示例

    • // core/useFetch.ts
      import { ref } from 'vue';
      
      export function useFetch<T>(url: string) {
        const data = ref<T | null>(null);
        const error = ref<Error | null>(null);
        const loading = ref(false);
      
        const fetchData = async () => {
          try {
            loading.value = true;
            const response = await fetch(url);
            data.value = await response.json();
          } catch (err) {
            error.value = err as Error;
          } finally {
            loading.value = false;
          }
        };
      
        return { data, error, loading, fetchData };
      }
      

2. Domain Hooks (领域层)

  • 功能:封装与业务逻辑相关的功能(如用户、订单、商品等)。

  • 示例

    • // domain/useUser.ts
      import { useFetch } from '../core/useFetch';
      
      interface User {
        id: number;
        name: string;
        email: string;
      }
      
      export function useUser(userId: number) {
        const { data, error, loading, fetchData } = useFetch<User>(`/api/users/${userId}`);
      
        const updateUser = async (userData: Partial<User>) => {
          // 更新用户逻辑
        };
      
        return { user: data, error, loading, fetchUser: fetchData, updateUser };
      }
      

3. UI ****Hooks (UI 层)

  • 功能:处理与 UI 组件相关的逻辑(如表单、模态框、分页等)。

  • 示例

    • // ui/useForm.ts
      import { ref } from 'vue';
      
      export function useForm<T extends Record<string, any>>(initialValues: T) {
        const form = ref<T>({ ...initialValues });
        const errors = ref<Record<string, string>>({});
      
        const validate = () => {
          // 表单验证逻辑
        };
      
        return { form, errors, validate };
      }
      

4. Shared Hooks (共享层)

  • 功能:跨项目或模块共享的 Hooks(如鉴权、配置等)。

  • 示例

    • // shared/useAuth.ts
      import { useLocalStorage } from '../core/useLocalStorage';
      
      export function useAuth() {
        const { value: token } = useLocalStorage<string>('authToken', '');
      
        const login = async (credentials: { username: string; password: string }) => {
          // 登录逻辑
        };
      
        const logout = () => {
          // 登出逻辑
        };
      
        return { token, login, logout };
      }
      

四、类型管理

  1. 内联类型简单的类型可以直接内联在 Hooks 文件中。

    1. // core/useFetch.ts
      interface FetchResult<T> {
        data: Ref<T | null>;
        error: Ref<Error | null>;
        loading: Ref<boolean>;
      }
      
  2. 独立类型文件复杂或共享的类型可以集中管理在 types/ 目录下。

    1. // types/hooks.d.ts
      export interface User {
        id: number;
        name: string;
        email: string;
      }
      
  3. 全局类型扩展通过 shims-vue.d.ts 扩展全局类型。

    1. // shims-vue.d.ts
      declare module 'vue' {
        interface ComponentCustomProperties {
          $myGlobalHook: () => void;
        }
      }
      

五、工具与规范

ESLint和Prettier的使用方式可以参考这篇文档内容:万字长文解析 UniApp+Vue3+TS跨端应用搭建指南,从0到1打造企业级跨端应用在跨端开发日益成为主流的今天,如何 - 掘金

  1. ESLint: 使用 ESLint 强制类型注解和代码规范。

    1. {
        "rules": {
          "@typescript-eslint/explicit-function-return-type": "error"
        }
      }
      
  2. Prettier: 统一代码格式化风格。

  3. 测试: 为 Hooks 编写单元测试,确保功能稳定。

    1. // tests/hooks/useFetch.spec.ts
      import { useFetch } from '@/hooks/core/useFetch';
      import { describe, it, expect } from 'vitest';
      
      describe('useFetch', () => {
        it('should fetch data successfully', async () => {
          const { fetchData } = useFetch<{ name: string }>('/api/test');
          await fetchData();
          expect(data.value).toEqual({ name: 'Test' });
        });
      });
      

六、总结

在大型项目中,通过分层管理、模块化设计和类型安全,可以高效组织和管理 Vue 3 Hooks。核心原则是:

  • 将 Hooks 分为核心层、领域层、UI 层和共享层;
  • 使用 TypeScript 确保类型安全;
  • 通过工具和规范提升代码质量。