Search K
Appearance
Appearance
📊 SEO元描述:2024年最新Vue3响应式API教程,详解ref、reactive、computed、watch用法。包含完整性能优化案例,适合Vue3开发者掌握响应式编程核心技术。
核心关键词:Vue3响应式API、ref reactive区别、Vue3 computed、Vue3 watch、前端响应式编程
长尾关键词:ref和reactive怎么选择、Vue3响应式原理、computed缓存机制、watchEffect用法、响应式数据最佳实践
通过本节响应式API深入,你将系统性掌握:
Vue3响应式API是什么?这是现代前端开发的核心技术。Vue3的响应式系统基于ES6 Proxy实现,提供了更强大、更灵活的响应式编程能力。
💡 核心理念:Vue3的响应式系统将数据变化自动转换为视图更新,让开发者专注于业务逻辑而非手动DOM操作。
ref是Vue3中最基础的响应式API,主要用于包装基本类型数据:
// 🎉 ref基础用法
import { ref, isRef, unref } from 'vue'
export default {
setup() {
// 创建响应式引用
const count = ref(0)
const message = ref('Hello Vue3')
const isVisible = ref(true)
// 访问和修改值需要使用.value
console.log(count.value) // 0
count.value = 10
// ref也可以包装对象
const user = ref({
name: 'John',
age: 25
})
// 修改对象属性
user.value.name = 'Jane'
user.value.age = 26
// 工具函数
console.log(isRef(count)) // true
console.log(unref(count)) // 获取原始值
return {
count,
message,
isVisible,
user
}
}
}<!-- 🎉 模板中的ref自动解包 -->
<template>
<div>
<!-- 模板中自动解包,无需.value -->
<p>计数: {{ count }}</p>
<p>消息: {{ message }}</p>
<button @click="count++">增加</button>
<button @click="message = '新消息'">更新消息</button>
</div>
</template>reactive用于创建对象的深度响应式代理:
// 🎉 reactive深度响应式
import { reactive, isReactive, toRaw } from 'vue'
export default {
setup() {
// 创建响应式对象
const state = reactive({
count: 0,
user: {
name: 'John',
profile: {
email: 'john@example.com',
settings: {
theme: 'dark'
}
}
},
items: [1, 2, 3]
})
// 直接修改属性,无需.value
state.count++
state.user.name = 'Jane'
state.user.profile.settings.theme = 'light'
// 数组操作也是响应式的
state.items.push(4)
state.items.splice(0, 1)
// 添加新属性也是响应式的
state.newProperty = 'new value'
// 工具函数
console.log(isReactive(state)) // true
console.log(toRaw(state)) // 获取原始对象
return {
state
}
}
}选择ref还是reactive取决于具体的使用场景:
// 🎉 ref vs reactive选择指南
import { ref, reactive } from 'vue'
export default {
setup() {
// ✅ 使用ref的场景
// 1. 基本类型数据
const count = ref(0)
const message = ref('hello')
const isLoading = ref(false)
// 2. 需要重新赋值的对象
const user = ref(null)
// 可以整体替换
user.value = { name: 'John', age: 25 }
user.value = { name: 'Jane', age: 30 }
// 3. 数组需要整体替换时
const items = ref([])
items.value = [1, 2, 3]
items.value = [4, 5, 6]
// ✅ 使用reactive的场景
// 1. 复杂对象状态
const formData = reactive({
username: '',
email: '',
profile: {
firstName: '',
lastName: ''
}
})
// 2. 不需要整体替换的对象
const appState = reactive({
currentUser: null,
settings: {},
cache: new Map()
})
// 3. 组件的本地状态
const localState = reactive({
isModalOpen: false,
selectedItems: [],
filters: {}
})
return {
count,
message,
isLoading,
user,
items,
formData,
appState,
localState
}
}
}选择原则:
computed在Composition API中提供了强大的计算能力:
// 🎉 computed高级应用
import { ref, reactive, computed } from 'vue'
export default {
setup() {
const state = reactive({
firstName: 'John',
lastName: 'Doe',
items: [
{ id: 1, name: 'Apple', price: 1.2, quantity: 3 },
{ id: 2, name: 'Banana', price: 0.8, quantity: 5 },
{ id: 3, name: 'Orange', price: 1.5, quantity: 2 }
]
})
// 1. 基础计算属性
const fullName = computed(() => {
return `${state.firstName} ${state.lastName}`
})
// 2. 复杂计算逻辑
const totalPrice = computed(() => {
return state.items.reduce((total, item) => {
return total + (item.price * item.quantity)
}, 0).toFixed(2)
})
// 3. 可写计算属性
const fullNameWritable = computed({
get() {
return `${state.firstName} ${state.lastName}`
},
set(value) {
const names = value.split(' ')
state.firstName = names[0] || ''
state.lastName = names[1] || ''
}
})
// 4. 依赖其他计算属性
const displayInfo = computed(() => {
return `${fullName.value} - 总价: $${totalPrice.value}`
})
// 5. 条件计算
const expensiveItems = computed(() => {
return state.items.filter(item => item.price > 1.0)
})
return {
state,
fullName,
totalPrice,
fullNameWritable,
displayInfo,
expensiveItems
}
}
}Vue3提供了两种侦听器:watch和watchEffect
// 🎉 watch和watchEffect全面应用
import { ref, reactive, watch, watchEffect } from 'vue'
export default {
setup() {
const count = ref(0)
const user = reactive({
name: 'John',
age: 25,
profile: {
email: 'john@example.com'
}
})
// 1. 基础watch用法
watch(count, (newValue, oldValue) => {
console.log(`count从 ${oldValue} 变为 ${newValue}`)
})
// 2. 侦听对象属性
watch(() => user.name, (newName, oldName) => {
console.log(`用户名从 ${oldName} 变为 ${newName}`)
})
// 3. 深度侦听
watch(user, (newUser, oldUser) => {
console.log('用户对象发生变化:', newUser)
}, { deep: true })
// 4. 立即执行
watch(count, (value) => {
console.log('当前计数:', value)
}, { immediate: true })
// 5. 侦听多个数据源
watch([count, () => user.name], ([newCount, newName], [oldCount, oldName]) => {
console.log(`计数: ${oldCount} -> ${newCount}`)
console.log(`姓名: ${oldName} -> ${newName}`)
})
// 6. watchEffect自动依赖收集
watchEffect(() => {
console.log(`用户 ${user.name} 的年龄是 ${user.age}`)
// 自动追踪user.name和user.age的变化
})
// 7. 清理副作用
const stopWatcher = watchEffect((onInvalidate) => {
const timer = setTimeout(() => {
console.log('定时器执行')
}, 1000)
// 清理函数
onInvalidate(() => {
clearTimeout(timer)
})
})
// 手动停止侦听器
// stopWatcher()
return {
count,
user
}
}
}watch vs watchEffect对比:
💼 最佳实践:对于简单的副作用使用watchEffect,需要精确控制或获取旧值时使用watch。
通过本节响应式API深入的学习,你已经掌握:
A: 对于基本类型,ref的性能更好。对于对象,reactive提供了更自然的API。选择应该基于使用场景而非性能考虑。
A: reactive基于Proxy实现,而Proxy只能代理对象。基本类型需要包装在对象中,这正是ref的作用。
A: computed有缓存机制,只有依赖变化时才重新计算;method每次调用都会执行。computed适合计算派生状态,method适合事件处理。
A: 在组件卸载时自动清理,或手动调用watch返回的停止函数。使用watchEffect时注意清理副作用。
A: 当你需要执行副作用且不关心具体的变化值时使用watchEffect;需要精确控制侦听源或获取旧值时使用watch。
// ✅ 性能优化最佳实践
import { ref, reactive, shallowRef, shallowReactive, readonly } from 'vue'
// 1. 大型数据结构使用shallowRef
const largeData = shallowRef({
// 大量数据,只需要根级别响应式
})
// 2. 只读数据使用readonly
const config = readonly({
apiUrl: 'https://api.example.com',
version: '1.0.0'
})
// 3. 避免不必要的深度响应式
const shallowState = shallowReactive({
// 只有第一层是响应式的
})"掌握Vue3的响应式API就是掌握了现代前端开发的核心技能。记住:选择合适的API,理解其工作原理,才能写出高性能的Vue3应用。"