Vue3 Composition API实战:响应式原理与组件逻辑复用方案

Vue3 Composition API与Options API的核心差异

Vue3引入Composition API是前端开发领域的重要演进。Options API通过data、methods、computed、watch等选项组织代码,组件逻辑分散在不同选项中,大型组件维护困难。Composition API以setup函数为入口,将相关逻辑聚合在一起,同一功能的响应式数据、计算属性和方法可以相邻编写,代码可读性和复用性显著提升。

Composition API的基础是ref和reactive两个响应式API。ref用于包装基本类型,通过.value访问;reactive用于包装对象,返回Proxy代理。底层依赖Proxy实现响应式追踪,相比Vue2的Object.defineProperty,能检测属性增删和数组索引修改。前端工程化实践中,Composition API配合TypeScript可以获得完整的类型推导体验。

响应式原理:Proxy与依赖追踪机制

Vue3响应式系统的核心是Proxy代理和依赖收集。reactive函数用Proxy包装目标对象,get拦截器中调用track收集依赖,set拦截器中调用trigger触发更新。effect函数注册副作用,当依赖的响应式数据变化时重新执行。

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

// ref:基本类型响应式
const count = ref(0)
count.value++  // 通过.value访问和修改

// reactive:对象响应式
const state = reactive({
  user: { name: '张三', age: 25 },
  list: []
})
state.user.name = '李四'  // 直接修改,触发更新
state.list.push('item')  // 数组操作也响应式

// computed:计算属性
const doubleCount = computed(() => count.value * 2)
const filteredList = computed(() => 
  state.list.filter(item => item.active)
)

// watch:侦听特定数据源
watch(count, (newVal, oldVal) => {
  console.log(`count: ${oldVal} -> ${newVal}`)
}, { immediate: true })

// watchEffect:自动追踪依赖
watchEffect(() => {
  console.log(`当前用户: ${state.user.name}, 数量: ${count.value}`)
})

组件逻辑复用:Composable函数设计模式

Composition API最大的优势是逻辑复用。将可复用逻辑封装为Composable函数(约定use开头命名),在多个组件中调用。相比Vue2的Mixin,Composable函数具有明确的输入输出、无隐式依赖、无命名冲突,是更可靠的复用方案。组件库设计中大量使用Composable函数封装交互逻辑。

常用Composable函数实现

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

// 鼠标位置追踪
export function useMousePosition() {
  const x = ref(0)
  const y = ref(0)

  const update = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  return { x, y }
}

// 防抖函数
export function useDebounce<T>(value: Ref<T>, delay = 300): Ref<T> {
  const debounced = ref(value.value) as Ref<T>
  let timer: ReturnType<typeof setTimeout>;

  watch(value, (val) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      debounced.value = val
    }, delay)
  })

  return debounced
}

// 异步数据加载
export function useFetch<T>(url: string) {
  const data = ref<T | null>(null)
  const error = ref<string | null>(null)
  const loading = ref(false)

  const execute = async () => {
    loading.value = true
    error.value = null
    try {
      const res = await fetch(url)
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      data.value = await res.json()
    } catch (e) {
      error.value = (e as Error).message
    } finally {
      loading.value = false
    }
  }

  onMounted(execute)
  return { data, error, loading, execute }
}

在组件中使用Composable:

<script setup lang="ts">
import { useMousePosition, useFetch } from './composables'

interface User {
  id: number
  name: string
  email: string
}

const { x, y } = useMousePosition()
const { data: users, error, loading, execute: refresh } = useFetch<User[]>('/api/users')
</script>

<template>
  <div>
    <p>鼠标位置: {{ x }}, {{ y }}</p>
    <button @click="refresh">刷新数据</button>
    <div v-if="loading">加载中...</div>
    <div v-else-if="error">{{ error }}</div>
    <ul v-else>
      <li v-for="user in users" :key="user.id">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  </div>
</template>

组件间状态管理:Pinia与Composition API集成

小型应用可以直接用Composable函数共享状态,中大型应用推荐使用Pinia。Pinia是Vue3官方状态管理库,API完全基于Composition API设计,支持TypeScript实战中的完整类型推导和开发工具集成。

// stores/user.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useUserStore = defineStore('user', () => {
  const token = ref('')
  const userInfo = ref<{ id: number; name: string } | null>(null)
  const isLoggedIn = computed(() => !!token.value)

  const login = async (username: string, password: string) => {
    const res = await api.post('/auth/login', { username, password })
    token.value = res.data.token
    userInfo.value = res.data.user
    localStorage.setItem('token', token.value)
  }

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

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

// 组件中使用
const userStore = useUserStore()
await userStore.login('admin', 'password')
console.log(userStore.isLoggedIn)

性能优化:shallowRef与标记跳过响应式

并非所有数据都需要深层响应式。大型列表、图表数据、第三方库对象可以用shallowRef或markRaw跳过深层代理,减少内存占用和初始化开销。Web性能优化中合理使用这两个API可以显著降低大型组件的渲染开销。shallowRef只对.value赋值触发响应,内部属性变化不追踪。markRaw标记对象永远不被代理。

import { shallowRef, markRaw } from 'vue'

// 大数据列表用shallowRef,避免深度代理性能开销
const bigList = shallowRef(new Array(10000).fill(0).map((_, i) => ({ id: i })))

// 替换整个数组才触发更新
bigList.value = newArrayData

// 第三方实例用markRaw
const chart = markRaw(echarts.init(domElement))

响应式系统的性能开销与数据规模成正比。合理使用shallowRef和markRaw,在不牺牲功能的前提下优化渲染性能。开发环境可以用Vue Devtools的Performance面板分析组件更新频率和耗时,定位性能瓶颈。跨端小程序开发中,响应式数据量直接影响渲染性能,更需要关注这些优化手段。响应式布局场景下,窗口尺寸等频繁变化的数据适合用shallowRef避免不必要的深度追踪。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3compositionapi-shi-zhan-xiang-ying-shi-yuan-li-yu-zu/

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

相关推荐