Vue3组合式API(Composition API)是对Options API的根本性重构,解决了大型组件中逻辑分散、复用困难的问题。通过setup函数和响应式API,开发者能按功能而非选项类型组织代码。本文从响应式原理到自定义Hook设计,覆盖Vue3生态中组合式API的实际工程用法。
响应式系统核心:ref与reactive对比
Vue3的响应式系统基于Proxy实现,替代了Vue2的Object.defineProperty方案。ref和reactive是两种创建响应式数据的基础API,使用场景有别。
import { ref, reactive, computed, watch } from 'vue'
// ref用于基本类型
const count = ref(0)
console.log(count.value) // 访问需.value
count.value++ // 修改需.value
// reactive用于对象和数组
const state = reactive({
user: { name: '张三', age: 28 },
list: ['Vue3', 'React', 'Angular'],
filter: 'all'
})
// reactive对象直接访问,无需.value
console.log(state.user.name)
state.list.push('Svelte')
// 使用toRefs解构reactive保持响应性
import { toRefs } from 'vue'
const { user, list } = toRefs(state)
reactive的局限在于无法直接解构——解构后失去响应性,需要用toRefs包装。ref虽然没有这个限制,但模板中会自动解包,脚本中需要手动.value。实际开发中,对象状态用reactive,计数器、布尔开关等基本类型用ref,这样区分清晰。
computed计算属性与watch侦听器
computed创建缓存计算值,只有依赖变化时才重新计算。watch侦听特定响应式数据的变化,执行副作用。
import { ref, reactive, computed, watch, watchEffect } from 'vue'
const products = reactive([
{ name: 'MacBook Pro', price: 14999, stock: 15 },
{ name: 'iPad Air', price: 4799, stock: 0 },
{ name: 'AirPods Pro', price: 1899, stock: 42 }
])
const filterInStock = ref(false)
// computed自动追踪依赖
const filteredProducts = computed(() => {
if (filterInStock.value) {
return products.filter(p => p.stock > 0)
}
return products
})
const totalPrice = computed(() =>
filteredProducts.value.reduce((sum, p) => sum + p.price * p.stock, 0)
)
// watch监听特定数据
watch(
() => products.length,
(newLen, oldLen) => {
console.log(`商品数量变化: ${oldLen} -> ${newLen}`)
}
)
// watchEffect自动收集依赖
watchEffect(() => {
console.log(`当前筛选结果: ${filteredProducts.value.length}件商品`)
})
computed与methods的区别在于缓存机制——computed在依赖未变化时返回缓存值,methods每次调用都重新执行。watch的lazy特性意味着默认不在初始化时执行,需要立即执行时设置immediate: true。
自定义组合式函数:逻辑复用的标准模式
自定义Hook(组合式函数)是Vue3中逻辑复用的推荐方式。命名以use开头,返回响应式数据和方法。相比Vue2的mixin,组合式函数具有明确的输入输出,不存在命名冲突和来源不透明的问题。
import { ref, onMounted, onUnmounted } from 'vue'
// 自定义Hook:窗口尺寸监听
export function useWindowSize() {
const width = ref(window.innerWidth)
const height = ref(window.innerHeight)
const update = () => {
width.value = window.innerWidth
height.value = window.innerHeight
}
onMounted(() => {
window.addEventListener('resize', update)
})
onUnmounted(() => {
window.removeEventListener('resize', update)
})
return { width, height }
}
// 自定义Hook:防抖搜索
import { ref, watch } from 'vue'
export function useDebounce(initialValue, delay = 300) {
const value = ref(initialValue)
const debounced = ref(initialValue)
let timer = null
watch(value, (newVal) => {
clearTimeout(timer)
timer = setTimeout(() => {
debounced.value = newVal
}, delay)
})
const cancel = () => {
clearTimeout(timer)
}
return { value, debounced, cancel }
}
使用自定义Hook的组件代码简洁清晰:
<script setup>
import { useWindowSize, useDebounce } from './composables'
const { width, height } = useWindowSize()
const { value: keyword, debounced: debouncedKeyword } = useDebounce('', 500)
</script>
<template>
<div>
<p>窗口尺寸: {{ width }} × {{ height }}</p>
<input v-model="keyword" placeholder="搜索..." />
<p>实际搜索词: {{ debouncedKeyword }}</p>
</div>
</template>
响应式布局与组件库设计中的响应式
在前端工程化实践中,响应式系统需要配合CSS媒体查询实现响应式布局。Vue3的响应式数据可以动态控制布局类名:
import { ref, computed } from 'vue'
const screenWidth = ref(window.innerWidth)
const layoutClass = computed(() => {
if (screenWidth.value < 768) return 'layout-mobile'
if (screenWidth.value < 1200) return 'layout-tablet'
return 'layout-desktop'
})
const isMobile = computed(() => screenWidth.value < 768)
组件库设计中,响应式数据传递遵循单向数据流原则。父组件通过props传递数据,子组件通过emit通知变更。对于跨层级共享状态,使用provide/inject而非层层传递props:
import { provide, inject, reactive, readonly } from 'vue'
// 父组件
const themeConfig = reactive({
primaryColor: '#1890ff',
mode: 'light'
})
provide('theme', readonly(themeConfig))
// 子组件(任意深度)
const theme = inject('theme')
// theme是只读的,子组件不能直接修改
// 通过provide的方法修改
const setTheme = inject('setTheme')
setTheme({ primaryColor: '#52c41a' })
TypeScript实战:组合式API类型推导
Vue3的TypeScript支持在组合式API中表现优秀。ref和reactive会自动推导类型,复杂场景可手动标注泛型。
import { ref, reactive, computed } from 'vue'
interface User {
id: number
name: string
email: string
roles: string[]
}
// ref泛型标注
const currentUser = ref<User | null>(null)
// reactive自动推导
const form = reactive<Partial<User>>({
name: '',
email: ''
})
// computed返回类型自动推导
const isAdmin = computed(() =>
currentUser.value?.roles.includes('admin') ?? false
)
// 自定义Hook的泛型支持
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
try {
const res = await fetch(url)
data.value = await res.json()
} catch (e) {
error.value = e instanceof Error ? e.message : 'Unknown error'
} finally {
loading.value = false
}
}
return { data, error, loading, execute }
}
泛型组合式函数配合TypeScript的窄化(narrowing)能实现编译期类型安全。在Web性能优化层面,合理的响应式数据拆分能减少不必要的重渲染——将不常变化的数据与频繁变化的数据分离,避免大范围依赖触发。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-zu-he-shi-api-shen-du-shi-zhan-xiang-ying-shi-xi-tong/