Vue3组合式API设计模式:Composable函数与状态管理最佳实践

Vue3组合式API为什么是现代前端工程化的基础

Vue3的组合式API(Composition API)不只是Options API的替代写法,它改变了前端代码的组织方式——从按选项类型分散到按逻辑关注点聚合。当组件逻辑复杂度上升时,Options API的data、methods、computed分散在不同选项中,同一业务逻辑的代码被割裂到多个位置,维护成本急剧上升。组合式API通过Composable函数将相关逻辑封装在一起,是实现大型前端项目可维护性的关键。

Composable设计模式:从逻辑提取到可复用单元

Composable(可组合函数)是组合式API的核心封装单元。一个良好的Composable应该满足:单一职责、显式参数、清晰的返回值类型、自动清理副作用。

基础模式:数据获取Composable

// composables/useFetch.ts
import { ref, shallowRef, type Ref } from 'vue'

interface UseFetchOptions<T> {
  immediate?: boolean
  initialData?: T
  onSuccess?: (data: T) => void
  onError?: (error: Error) => void
}

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

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

  let abortController: AbortController | null = null

  const execute = async () => {
    abortController?.abort()
    abortController = new AbortController()
    
    isLoading.value = true
    error.value = null

    try {
      const resolvedUrl = typeof url === 'string' ? url : url.value
      const response = await fetch(resolvedUrl, {
        signal: abortController.signal
      })
      
      if (!response.ok) throw new Error(`HTTP ${response.status}`)
      
      data.value = await response.json() as T
      options.onSuccess?.(data.value)
    } catch (e) {
      if (e instanceof Error && e.name !== 'AbortError') {
        error.value = e
        options.onError?.(e)
      }
    } finally {
      isLoading.value = false
    }
  }

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

  return {
    data: data as Readonly<Ref<T | null>>,
    error: error as Readonly<Ref<Error | null>>,
    isLoading: isLoading as Readonly<Ref<boolean>>,
    execute
  }
}

使用示例:

// 在组件中使用
const { data: users, isLoading, error, execute: refresh } = useFetch<User[]>(
  '/api/users',
  { immediate: true }
)

Composable组合模式:多个Composable协同

复杂业务场景下,多个Composable需要协同工作。典型例子:分页列表 = 数据获取 + 分页状态 + 筛选条件。

// composables/usePagination.ts
import { ref, computed, watch, type Ref } from 'vue'

interface UsePaginationOptions {
  pageSize: number
  total: Ref<number>
}

export function usePagination(options: UsePaginationOptions) {
  const currentPage = ref(1)
  const pageSize = ref(options.pageSize)

  const totalPages = computed(() =>
    Math.ceil(options.total.value / pageSize.value)
  )

  const offset = computed(() =>
    (currentPage.value - 1) * pageSize.value
  )

  // 页码变化时重置到第1页
  watch(pageSize, () => { currentPage.value = 1 })

  const goToPage = (page: number) => {
    currentPage.value = Math.max(1, Math.min(page, totalPages.value))
  }

  return { currentPage, pageSize, totalPages, offset, goToPage }
}

// 在组件中组合使用
const total = ref(0)
const { currentPage, pageSize, offset } = usePagination({ pageSize: 20, total })
const { data: items } = useFetch(computed(() =>
  `/api/items?offset=${offset.value}&limit=${pageSize.value}`
))

状态管理:Pinia + Composable的分层架构

Pinia是Vue3官方推荐的状态管理库。在大型项目中,推荐的状态管理分层架构:

  • 局部状态:组件内部ref/reactive,不需要跨组件共享
  • Composable状态:通过Composable共享,适用于强关联的功能模块(如表单校验、分页)
  • 全局Store:Pinia Store管理跨模块共享的业务数据(如用户信息、权限、全局配置)
// stores/user.ts
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', () => {
  const token = ref(localStorage.getItem('token') || '')
  const userInfo = shallowRef<UserInfo | null>(null)

  const isLoggedIn = computed(() => !!token.value)

  const login = async (credentials: LoginParams) => {
    const res = await authApi.login(credentials)
    token.value = res.token
    userInfo.value = res.user
    localStorage.setItem('token', res.token)
  }

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

  return { token, userInfo, isLoggedIn, login, logout }
})

关键设计原则:Pinia Store只管理全局共享状态,功能模块内的状态用Composable封装。避免把所有状态都塞进Store——那会让Store变成巨型全局变量池,失去模块化的意义。

TypeScript类型安全:泛型Composable的进阶写法

TypeScript与组合式API结合能显著提升开发体验和代码可靠性。泛型Composable是进阶写法:

// composables/useTable.ts — 通用表格逻辑
interface TableState<T> {
  items: T[]
  selected: Set<number>
  sortBy: keyof T | null
  sortOrder: 'asc' | 'desc'
}

export function useTable<T extends Record<string, unknown>>(
  fetchFn: (params: TableParams) => Promise<TableResponse<T>>
) {
  const state = reactive<TableState<T>>({
    items: [],
    selected: new Set(),
    sortBy: null,
    sortOrder: 'asc'
  })

  const sortedItems = computed(() => {
    if (!state.sortBy) return state.items
    return [...state.items].sort((a, b) => {
      const aVal = a[state.sortBy!]
      const bVal = b[state.sortBy!]
      const modifier = state.sortOrder === 'asc' ? 1 : -1
      return aVal < bVal ? -modifier : aVal > bVal ? modifier : 0
    })
  })

  const toggleSort = (key: keyof T) => {
    if (state.sortBy === key) {
      state.sortOrder = state.sortOrder === 'asc' ? 'desc' : 'asc'
    } else {
      state.sortBy = key
      state.sortOrder = 'asc'
    }
  }

  return { state: readonly(state), sortedItems, toggleSort }
}

响应式陷阱与性能优化

组合式API有几个常见的响应式陷阱:

陷阱1:解构reactive丢失响应性

// 错误:解构后丢失响应性
const state = reactive({ count: 0, name: '' })
const { count } = state  // count是普通number,不是ref

// 正确:使用toRefs
const { count } = toRefs(state)  // count是ref

陷阱2:watch监听reactive对象的属性

// 错误:无法直接监听reactive对象的某个属性
watch(() => state.count, (val) => { ... })  // 不会触发

// 正确方式1:getter函数
watch(() => state.count, (val) => { ... })  // Vue3.2+支持

// 正确方式2:用ref代替
const count = ref(0)
watch(count, (val) => { ... })

性能优化:shallowRef和shallowReactive

当数据对象很大但不需要深度响应时,使用shallow版本避免深层代理的性能开销:

// 大型列表数据无需深度响应
const largeList = shallowRef<Item[]>([])

// 更新时替换整个数组引用
largeList.value = [...largeList.value, newItem]  // 触发更新
largeList.value.push(newItem)                     // 不触发更新!

Vue3组合式API的核心价值在于:通过Composable函数将逻辑封装为可复用、可组合、类型安全的单元,配合Pinia实现分层状态管理,再辅以TypeScript泛型约束——这套架构能支撑大型前端项目从开发到长期维护的全周期需求。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-api-she-ji-mo-shi-composable-han-shu-yu/

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

相关推荐