深度监听 ref 和 reactive 的区别详解
以下从响应式原理、监听方式和实际表现三个维度,分析二者的核心区别:
一、ref 的深度监听(示例代码)
<template>
<div class="person">
<p>嵌套值:{{ refObj.nested.value }}</p>
<button @click="refObj.nested.value = '新的值'">修改值</button>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
const refObj = ref({
nested: {
value: "初始值"
}
});
watch(refObj, (newVal) => {
console.log("ref 嵌套变化:", newVal.nested.value);
}, { deep: true }); // 必须显式设置 deep: true
watch(refObj.value, (newVal) => {
console.log("ref 嵌套变化:", newVal.nested.value);
}); // 若改为监听refObj.value(即 reactive 对象),则无需 deep
</script>
关键点:
1. ref 的存储方式:
ref 会将对象包裹在 .value
属性中,底层自动用 reactive
转换嵌套对象。(即 refObj.value
是 reactive 代理对象)
2. 监听 ref 的特性
- 直接监听
refObj
(而非.value
)时,需要显式设置deep: true
才能捕获嵌套属性变化。若不设置deep
,仅当整个.value
被替换时才会触发监听。 - 等价替代方案
若改为监听refObj.value
(即 reactive 对象),则无需 deep:
二、reactive 的深度监听(示例代码)
const reactiveObj = reactive({
nested: {
value: "初始值"
}
});
watch(() => reactiveObj, (newVal) => {
console.log("reactive 嵌套变化:", newVal.nested.value);
}, { deep: true }); // deep:true 在此处是冗余的
关键点:
1. reactive 的深度响应性
reactive 创建的代理对象默认支持深层级响应,修改任意嵌套属性都会触发更新。
2. 监听 reactive 的特性
- 直接监听 reactiveObj 时,无需 deep 即可自动深度监听。
示例代码中的() => reactiveObj
是冗余写法,直接传递reactiveObj
即可:
watch(reactiveObj, (newVal) => {
console.log("reactiveObj 嵌套变化:", newVal.nested.value);
}); // 直接监听 reactiveObj 时,无需 deep 即可自动深度监听
- 若监听一个返回
reactive
对象的函数(如() => reactiveObj
),需开启深度监听:{deep:true}
。
watch(()=>reactiveObj, (newVal) => {
console.log("reactiveObj 嵌套变化:", newVal.nested.value);
},{deep:true}); // 监听一个返回reactive对象的函数,deep:true 开启深度监听
- 若监听
reactiveObj.nested.value
时,需使用函数式写法()=>reactiveObj.nested.value
,无需开启深度监听。
watch(()=>reactiveObj.nested.value,(newVal,oldVal)=>{
console.log("reactiveObj 嵌套变化:", newVal,oldVal);
}); // 监听 reactiveObj.nested.value 时,需使用函数式写法,deep 选项无效
三、核心区别总结
特性 | ref (对象类型) | reactive |
---|---|---|
数据存储方式 | 包裹在 .value 中 | 直接代理对象 |
默认深度监听 | 否(需显式设置 deep) | 是 |
推荐监听方式 | 监听 .value (无需 deep) | 直接监听对象 |
适用场景 | 基本类型或需要替换整个对象 | 复杂对象,需深层级响应式 |
四、最佳实践建议
1. 优先使用 reactive
处理复杂对象时,reactive 的自动深度响应更简洁高效。
2. 明确 ref 的使用场景
当需要替换整个对象或处理基础类型时,选择 ref。
3. 简化 watch 配置
- 监听 ref 的 .value 替代显式 deep
- 直接传递 reactive 对象而非函数返回值
通过理解这些差异,可以更精准地选择响应式工具,并避免不必要的性能消耗。