02响应式基础
Vue 3 的响应式系统是其核心特性之一,它基于 ES6 的 Proxy 实现,相比 Vue 2 的 Object.defineProperty 有显著改进。
一、核心概念
1.1 什么是响应式?
当数据发生变化时,自动更新依赖该数据的视图或计算。
1.2 响应式原理
Vue 3 使用 Proxy 对象包装原始对象,拦截各种操作(读取、写入、删除等),并在变化时触发更新。
二、核心 API
2.1 ref()
用于创建响应式的基本类型或对象引用。
import { ref } from 'vue'
// 基本类型
const count = ref(0)
console.log(count.value) // 0
// 对象类型
const user = ref({
name: 'John',
age: 25
})
console.log(user.value.name) // John
// 模板中自动解套,无需 .value
// <div>{{ count }}</div>
2.2 reactive()
用于创建响应式的对象(包括数组)。
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'John',
hobbies: ['reading', 'coding']
}
})
// 直接访问,不需要 .value
console.log(state.count) // 0
state.count++ // 响应式更新
// 数组也是响应式的
state.user.hobbies.push('gaming')
2.3 ref() vs reactive()

三、响应式工具函数
3.1 toRef() 和 toRefs()
import { reactive, toRef, toRefs } from 'vue'
const state = reactive({
name: 'John',
age: 25
})
// 将 reactive 的属性转换为 ref
const nameRef = toRef(state, 'name')
// 将整个 reactive 对象的所有属性转换为 ref 对象
const { name, age } = toRefs(state)
// 在组合式函数中返回时很有用
function useFeature() {
const state = reactive({ x: 0, y: 0 })
return toRefs(state) // 解构后仍保持响应式
}
3.2 computed()
创建响应式的计算属性。
import { ref, computed } from 'vue'
const count = ref(0)
// 只读计算属性
const doubleCount = computed(() => count.value * 2)
// 可写的计算属性
const writableComputed = computed({
get: () => count.value * 2,
set: (val) => {
count.value = val / 2
}
})
3.3 watch() 和 watchEffect()
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
const user = reactive({ name: 'John', age: 25 })
// watch:显式指定监听源
watch(count, (newVal, oldVal) => {
console.log(`count changed: ${oldVal} -> ${newVal}`)
})
// 监听多个源
watch([count, () => user.name], ([newCount, newName], [oldCount, oldName]) => {
// 处理变化
})
// watchEffect:自动追踪依赖
watchEffect(() => {
console.log(`count: ${count.value}, name: ${user.name}`)
// 自动追踪 count.value 和 user.name
})
四、响应式进阶
4.1 浅层响应式
import { shallowRef, shallowReactive } from 'vue'
// shallowRef:只有 .value 的替换是响应式的
const shallowObj = shallowRef({ deep: { value: 1 } })
shallowObj.value = { deep: { value: 2 } } // 触发更新
shallowObj.value.deep.value = 3 // 不会触发更新
// shallowReactive:只有根层属性是响应式的
const shallowState = shallowReactive({
deep: { value: 1 }
})
shallowState.deep = { value: 2 } // 触发更新
shallowState.deep.value = 3 // 不会触发更新
4.2 只读代理
import { reactive, readonly } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original)
original.count++ // 正常工作
copy.count++ // 警告!不可修改
4.3 响应式转换
import { markRaw, isReactive, isRef, unref } from 'vue'
// markRaw:标记对象永远不会被转为响应式
const nonReactive = markRaw({ shouldNotBeReactive: true })
// 类型检查
const obj = reactive({})
console.log(isReactive(obj)) // true
console.log(isRef(ref(0))) // true
// unref:如果是 ref 返回其值,否则返回本身
const value = unref(maybeRef)
五、实际应用示例
5.1 组件中使用
<script setup> import { ref, reactive, computed } from 'vue' // 使用 ref 处理基本类型 const count = ref(0) // 使用 reactive 处理对象 const form = reactive({ username: '', password: '' }) // 计算属性 const isFormValid = computed(() => { return form.username.length > 0 && form.password.length > 0 }) // 方法 function increment() { count.value++ } // 监听 watch(count, (newValue) => { console.log('Count changed to:', newValue) }) </script> <template> <div> <p>Count: {{ count }}</p> <button @click="increment">Increment</button> <input v-model="form.username" placeholder="Username"> <input v-model="form.password" type="password" placeholder="Password"> <button :disabled="!isFormValid">Submit</button> </div> </template>
5.2 组合式函数
// useCounter.js import { ref, computed } from 'vue' export function useCounter(initialValue = 0) { const count = ref(initialValue) const double = computed(() => count.value * 2) function increment() { count.value++ } function decrement() { count.value-- } function reset() { count.value = initialValue } return { count, double, increment, decrement, reset } } // 在组件中使用 import { useCounter } from './useCounter' const { count, double, increment } = useCounter(10)
六、注意事项
-
响应式丢失:解构 reactive 对象会失去响应式,使用
toRefs解决 -
异步更新:响应式更新是异步的,使用
nextTick访问更新后的 DOM -
大数组性能:对超大数组使用响应式可能影响性能,考虑分页或虚拟滚动
-
循环引用:避免在响应式对象中创建循环引用
-
避免重复包装:不要将已响应式的对象再次用
reactive()包装
七、最佳实践
-
使用
ref处理基本类型,reactive处理对象 -
组合式函数返回时使用
toRefs保持响应式 -
合理使用
shallowRef/shallowReactive优化性能 -
复杂的计算使用
computed缓存结果 -
使用
watchEffect简化依赖追踪逻辑
Vue 3 的响应式系统提供了强大而灵活的工具,理解这些概念和 API 对于编写高效、可维护的 Vue 应用至关重要。

浙公网安备 33010602011771号