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/