目录
深度理解 Vue 3 Composition API
作者:文文
为什么需要 Composition API?
Composition API 相比 Options API 的优势: - 🎯 逻辑复用性更好(Composables) - 📦 代码组织更清晰 - 🔍 类型推断更准确(对 TypeScript 友好) - ⚡ 性能更优
核心概念
ref - 响应式引用
import { ref } from 'vue'
const count = ref(0)
// 在 JavaScript 中需要 .value
console.log(count.value) // 0
count.value++
// 在模板中自动解包
// <div>{{ count }}</div>
reactive - 响应式对象
import { reactive } from 'vue'
const state = reactive({
user: {
name: 'Alice',
age: 25
}
})
// 直接修改属性
state.user.name = 'Bob'
computed - 计算属性
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`
})
// 可写的计算属性
const fullNameWritable = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue) => {
const [first, last] = newValue.split(' ')
firstName.value = first
lastName.value = last
}
})
watch - 监听响应式数据
import { ref, watch } from 'vue'
const message = ref('Hello')
// 监听单个 ref
watch(message, (newValue, oldValue) => {
console.log(`Message changed from ${oldValue} to ${newValue}`)
})
// 监听多个源
watch(
[firstName, lastName],
([newFirst, newLast], [oldFirst, oldLast]) => {
console.log('Names changed')
}
)
// 监听深层属性
watch(
() => state.user.name,
(newName) => {
console.log(`User name is now ${newName}`)
}
)
// watchEffect - 自动追踪依赖
watchEffect(() => {
console.log(`Message is: ${message.value}`)
// 任何时候 message 改变,这个函数都会运行
})
组织代码 - Composables
Composables 是 Vue 3 中复用逻辑的最佳实践。
例子:useMouse
// composables/useMouse.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useMouse() {
const x = ref(0)
const y = ref(0)
const update = (event) => {
x.value = event.clientX
y.value = event.clientY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return { x, y }
}
使用 Composable
<template>
<div>
<p>Mouse position: {{ x }}, {{ y }}</p>
</div>
</template>
<script setup>
import { useMouse } from '@/composables/useMouse'
const { x, y } = useMouse()
</script>
生命周期钩子
import {
onMounted,
onUpdated,
onUnmounted,
onBeforeMount,
onBeforeUpdate,
onBeforeUnmount,
onErrorCaptured,
onActivated,
onDeactivated
} from 'vue'
// 组件挂载完成
onMounted(() => {
console.log('Component mounted!')
})
// 响应式数据更新后
onUpdated(() => {
console.log('Component updated!')
})
// 组件卸载前
onBeforeUnmount(() => {
console.log('Cleaning up...')
})
实战例子:数据获取 Composable
// composables/useFetch.js
import { ref, watch } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(true)
const fetchData = async () => {
loading.value = true
try {
const response = await fetch(url)
data.value = await response.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
}
watch(() => url, fetchData, { immediate: true })
return { data, error, loading }
}
性能优化
1. 减少不必要的响应性
// 不好:将整个大对象变为响应式
const hugeData = reactive({ /* 大量数据 */ })
// 好:只让需要响应的部分为响应式
const neededPart = ref(hugeData.neededField)
2. 使用 shallowRef 和 shallowReactive
const state = shallowReactive({
count: 0,
nested: { value: 0 }
})
// count 变化会触发更新,但 nested.value 不会
state.count++
state.nested.value++ // 这个不会触发更新
常见错误
错误 1:在模板中多次调用方法
<!-- 不好:每次渲染都调用 -->
<div>{{ expensiveCalculation() }}</div>
<!-- 好:使用 computed -->
<div>{{ cachedResult }}</div>
<script setup>
const cachedResult = computed(() => expensiveCalculation())
</script>
错误 2:忘记 .value
const count = ref(0)
// 在 script 中需要 .value
count.value++ // 正确
count++ // 错误
总结
Composition API 让 Vue 3 的代码更灵活、更易维护。掌握它是成为高级 Vue 开发者的关键。
文文,2026 年 3 月 16 日
