众所周知,Vue Hooks(通常指 Composition API 中的功能)是 Vue 3 引入的一种代码组织方式,用于更灵活地组合和复用逻辑。但是在项目中大量使用这种写法该如何更好的搭建结构呢?以下是可供参考实践的简单示例。
一、Hooks 组织原则
-
单一职责每个 Hook 应专注于完成单一功能,避免功能过重。
-
模块化将 Hooks 拆分为独立的模块,便于复用和维护。
-
类型安全 使用 TypeScript 明确类型,避免隐式
any
。 -
分层管理根据功能或业务逻辑将 Hooks 分层管理,避免混乱。
二、项目目录结构
以下是一个适用于大型项目的目录结构示例:
shell
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 (核心层)
-
功能:提供与业务无关的基础功能(如网络请求、事件监听、本地存储等)。
-
示例:
ts// 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 (领域层)
-
功能:封装与业务逻辑相关的功能(如用户、订单、商品等)。
-
示例:
ts// 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 组件相关的逻辑(如表单、模态框、分页等)。
-
示例:
ts// 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(如鉴权、配置等)。
-
示例:
ts// 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 }; }
四、类型管理
-
内联类型简单的类型可以直接内联在 Hooks 文件中。
ts// core/useFetch.ts interface FetchResult<T> { data: Ref<T | null>; error: Ref<Error | null>; loading: Ref<boolean>; }
-
独立类型文件 复杂或共享的类型可以集中管理在
types/
目录下。ts// types/hooks.d.ts export interface User { id: number; name: string; email: string; }
-
全局类型扩展 通过
shims-vue.d.ts
扩展全局类型。ts// shims-vue.d.ts declare module 'vue' { interface ComponentCustomProperties { $myGlobalHook: () => void; } }
五、工具与规范
ESLint和Prettier的使用方式可以参考这篇文档内容:万字长文解析 UniApp+Vue3+TS跨端应用搭建指南,从0到1打造企业级跨端应用在跨端开发日益成为主流的今天,如何 - 掘金
-
ESLint: 使用 ESLint 强制类型注解和代码规范。
ts{ "rules": { "@typescript-eslint/explicit-function-return-type": "error" } }
-
Prettier: 统一代码格式化风格。
-
测试: 为 Hooks 编写单元测试,确保功能稳定。
ts// 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 确保类型安全;
- 通过工具和规范提升代码质量。