Vue3组合式函数封装实战:从业务逻辑复用到类型安全的进阶模式

组合式函数在Vue3工程中解决什么问题

Vue3的组合式API(Composition API)相比选项式API最大的优势在于逻辑复用。Options API里同一个功能的data、computed、watch分散在多个选项中,跨组件复用只能靠mixin——而mixin存在命名冲突和来源不透明的致命缺陷。组合式函数(Composable)把相关状态和逻辑封装为独立函数,显式导入导出,彻底解决了mixin的问题。

一个合格的Composable需要满足三个条件:响应式数据可被消费者控制、副作用可清理、类型推断完整。

基础模式:带请求状态的数据拉取Composable

最常见的场景是异步数据获取,手动管理loading/error状态非常繁琐。封装一个通用方案:

import { ref, shallowRef, type Ref } from 'vue'

interface UseFetchOptions<T> {
  immediate?: boolean
  initialData?: T
  refetch?: boolean
}

interface UseFetchReturn<T> {
  data: Ref<T | undefined>
  error: Ref<Error | null>
  isLoading: Ref<boolean>
  execute: () => Promise<void>
}

export function useFetch<T>(
  url: string | Ref<string>,
  options: UseFetchOptions<T> = {}
): UseFetchReturn<T> {
  const data = shallowRef<T | undefined>(options.initialData)
  const error = ref<Error | null>(null)
  const isLoading = ref(false)

  const execute = async () => {
    isLoading.value = true
    error.value = null
    try {
      const resolvedUrl = typeof url === 'string' ? url : url.value
      const res = await fetch(resolvedUrl)
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      data.value = await res.json()
    } catch (e) {
      error.value = e instanceof Error ? e : new Error(String(e))
    } finally {
      isLoading.value = false
    }
  }

  if (options.immediate !== false) {
    execute()
  }

  return { data, error, isLoading, execute }
}

关键细节:data用shallowRef而非ref,避免大对象的深层响应式开销;url参数支持Ref类型,响应式URL变化时可以配合watch自动重新请求。

进阶模式:带防抖和取消的搜索Composable

搜索场景需要防抖、取消上一次请求、响应式参数联动。这是Composable设计能力的试金石:

import { ref, watch, onUnmounted, type Ref } from 'vue'

export function useDebouncedSearch<T>(
  searchFn: (query: string) => Promise<T[]>,
  query: Ref<string>,
  debounceMs = 300
) {
  const results = shallowRef<T[]>( [])
  const isSearching = ref(false)
  let abortController: AbortController | null = null
  let timer: ReturnType<typeof setTimeout>

  const search = async (q: string) => {
    // 取消上一次请求
    abortController?.abort()
    abortController = new AbortController()

    if (!q.trim()) {
      results.value = []
      return
    }

    isSearching.value = true
    try {
      results.value = await searchFn(q)
    } catch (e) {
      if (e instanceof DOMException && e.name === 'AbortError') return
      throw e
    } finally {
      isSearching.value = false
    }
  }

  watch(query, (newVal) => {
    clearTimeout(timer)
    timer = setTimeout(() => search(newVal), debounceMs)
  }, { immediate: true })

  // 组件卸载时清理
  onUnmounted(() => {
    clearTimeout(timer)
    abortController?.abort()
  })

  return { results, isSearching }
}

onUnmounted里清理定时器和请求,防止组件卸载后的内存泄漏和状态更新。这是Composable编写的基本准则——任何副作用都必须在组件卸载时清理。

跨组件状态共享的Composable模式

当多个组件需要共享同一份状态时,Composable可以通过模块级变量实现轻量级状态管理,无需引入Pinia:

// useAuth.ts - 全局认证状态
import { ref, computed, readonly } from 'vue'

// 模块级变量,所有调用共享同一份状态
const user = ref<{ id: string; name: string } | null>(null)
const token = ref<string | null>(null)
const isAuthenticated = computed(() => !!token.value)

export function useAuth() {
  const login = async (credentials: { username: string; password: string }) => {
    const res = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(credentials)
    })
    const data = await res.json()
    user.value = data.user
    token.value = data.token
    localStorage.setItem('token', data.token)
  }

  const logout = () => {
    user.value = null
    token.value = null
    localStorage.removeItem('token')
  }

  return {
    user: readonly(user),       // 外部只读,防止直接修改
    token: readonly(token),
    isAuthenticated,
    login,
    logout
  }
}

readonly包装返回值,确保状态只能通过暴露的方法修改,符合单一数据流原则。比Pinia轻量,适合不需要DevTools和插件机制的中等规模应用。

Composable的TypeScript类型设计原则

类型安全的Composable需要在泛型约束和类型推断间找平衡。过度标注会让代码冗长,依赖推断又可能在边界场景丢失类型。工程实践中的建议:公共API的入参和返回值显式标注,内部实现依赖推断。使用泛型约束确保类型之间的关系正确传播,不要到处用any强制绕过。

组合式函数是Vue3项目架构的基石。好的Composable封装让组件保持薄层只管渲染,业务逻辑全部下沉到可测试、可复用的函数中。掌握上述几种模式后,日常开发中80%的逻辑复用场景都能覆盖。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-han-shu-feng-zhuang-shi-zhan-cong-ye-wu-luo/

(0)
小编小编
上一篇 17小时前
下一篇 17小时前

相关推荐