Vue3 Composables组合式函数设计模式与逻辑复用实践

Vue3 Composables组合式函数是Vue3组合式API的核心设计模式,通过将组件逻辑封装为可复用的函数,解决Options API在复杂组件中逻辑分散、难以复用的问题。响应式布局、跨端小程序开发等场景中,Composables已经成为Vue3生态的标准逻辑复用方案。组件template中调用的数据和方法,在Composables中通过ref、reactive、computed等响应式API实现,函数返回响应式数据供组件消费。

组合式API响应式基础:ref与reactive

ref和reactive是Vue3响应式系统的两个基础API。ref用于包装基本类型值,通过.value访问和修改。reactive用于包装对象,返回Proxy代理对象,直接操作属性即可触发响应式更新。选择规则:基本类型用ref,对象用reactive,从API返回的响应式对象建议用ref包裹便于整体替换。

import { ref, reactive, computed } from 'vue'

// ref示例:计数器
const count = ref(0)
count.value++
console.log(count.value) // 1

// reactive示例:表单状态
const form = reactive({
  username: '',
  password: '',
  remember: false
})
form.username = 'admin'

// computed示例:派生状态
const isValid = computed(() => 
  form.username.length >= 3 && form.password.length >= 6
)

自定义Composable函数设计规范

Composable函数遵循统一命名规范:以use开头,返回响应式数据和方法。一个设计良好的Composable应当职责单一、无副作用、可独立测试。以下是一个完整的useFetch实现,封装数据请求逻辑,包含loading状态、错误处理和数据缓存。

import { ref, onUnmounted } from 'vue'

export function useFetch(url, options = {}) {
  const data = ref(null)
  const error = ref(null)
  const loading = ref(false)
  let controller = null

  async function execute() {
    loading.value = true
    error.value = null
    controller = new AbortController()
    
    try {
      const resp = await fetch(url, {
        ...options,
        signal: controller.signal
      })
      if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
      data.value = await resp.json()
    } catch (e) {
      if (e.name !== 'AbortError') {
        error.value = e.message
      }
    } finally {
      loading.value = false
    }
  }

  function cancel() {
    if (controller) controller.abort()
  }

  // 组件卸载时取消未完成请求
  onUnmounted(() => cancel())

  execute()
  return { data, error, loading, refetch: execute, cancel }
}

使用时在组件setup中直接调用,返回的响应式数据可在template中直接使用。onUnmounted生命周期钩子确保组件销毁时清理资源,避免内存泄漏。

逻辑复用模式:鼠标跟踪与窗口尺寸监听

Composables最大的价值在于逻辑复用。多个组件需要相同功能时,将逻辑提取到Composable函数中,各组件独立调用互不干扰。以下示例封装鼠标位置追踪和窗口尺寸监听两个常见场景。

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 }
}

// 窗口尺寸监听(带防抖)
export function useWindowSize(debounce = 200) {
  const width = ref(window.innerWidth)
  const height = ref(window.innerHeight)
  let timer = null

  function update() {
    clearTimeout(timer)
    timer = setTimeout(() => {
      width.value = window.innerWidth
      height.value = window.innerHeight
    }, debounce)
  }

  onMounted(() => window.addEventListener('resize', update))
  onUnmounted(() => {
    window.removeEventListener('resize', update)
    clearTimeout(timer)
  })

  return { width, height }
}

每个组件调用useMouse()会获得独立的x、y响应式引用,事件监听器的注册和清理由Composable内部管理。这种模式比Vue2的Mixin方案更清晰,不存在命名冲突和数据来源不透明的问题。

Composable组合与状态共享

多个Composable可以自由组合,形成更复杂的业务逻辑。当多个组件需要共享同一份状态时,可以将Composable的响应式状态提升到模块作用域,实现跨组件共享。

import { ref, computed } from 'vue'

// 全局共享的购物车状态(模块级单例)
const cart = ref([])
const cartOpen = ref(false)

export function useCart() {
  const totalItems = computed(() => 
    cart.value.reduce((sum, item) => sum + item.qty, 0)
  )
  const totalPrice = computed(() => 
    cart.value.reduce((sum, item) => sum + item.qty * item.price, 0)
  )

  function addItem(product, qty = 1) {
    const existing = cart.value.find(i => i.id === product.id)
    if (existing) {
      existing.qty += qty
    } else {
      cart.value.push({ ...product, qty })
    }
  }

  function removeItem(id) {
    cart.value = cart.value.filter(i => i.id !== id)
  }

  function clear() {
    cart.value = []
  }

  return { 
    cart, cartOpen, totalItems, totalPrice,
    addItem, removeItem, clear
  }
}

由于cart和cartOpen定义在模块作用域,所有调用useCart()的组件共享同一份状态,修改一处所有组件同步更新。这种模式适用于小型应用的简单状态共享。对于复杂的大型应用,推荐使用Pinia状态管理库,它基于组合式API设计,Devtools集成度更好,TypeScript类型推导更完善。

Composables设计的一个常见误区是将所有逻辑塞进一个函数。更好的做法是按功能拆分为多个小函数,再在组件中按需组合。单个Composable保持在50行以内,职责清晰,可测试性强,符合前端工程化的模块化设计原则。

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

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

相关推荐