Vue 3 对象监听与快照:ref 和 reactive 的关键差异

对象原地修改后,watch 回调中的 newValue 与 oldValue 通常指向同一个代理对象。watch 只负责通知响应式变化,不会自动保存历史快照。要获得完整的新旧状态,首先要区分"整体替换对象"和"修改对象属性"。

行为模型

ref 是带 .value 的容器,reactive 直接返回 Proxy。watch(refObj) 默认只监听 .value 的整体替换;修改嵌套属性需要开启 { deep: true }。

直接监听 reactive 对象时会隐式深度监听,因此属性变化能触发回调,但新旧值仍是同一个对象。

ts 复制代码
const aaa = ref({a: 1, b: 2})
const bbb = reactive({a: 1, b: 2})
watch(aaa, cb)                    // 监听 aaa.value 替换
watch(aaa, cb, { deep: true })   // 监听嵌套属性
watch(bbb, cb)                    // reactive 隐式深度监听
操作 是否触发 能否直接取得独立旧值
aaa.value = { ...aaa.value } 是 能
aaa.value.a = 2 开启 deep 后能 不能
bbb.a = 2 能 不能
重新赋值 reactive 变量 不能 不能

获取reactive对象完整快照

扁平对象可用 computed 生成浅快照:

ts 复制代码
const snapshot = computed(() => ({ ...bbb }))
watch(snapshot, (newValue, oldValue) => {
  console.log(oldValue, newValue)
})

但浅拷贝只隔离第一层。对象包含嵌套结构时,需要深拷贝,或通过 watch 手动维护上一份快照。structuredClone 不能直接克隆 Proxy;在 computed 中使用 toRaw 又可能丢失依赖追踪,因此克隆方式必须同时保证依赖读取和数据类型安全。

ts 复制代码
let previousSnapshot = structuredClone(toRaw(bbb))

watch(bbb, () => {
  const currentSnapshot = structuredClone(toRaw(bbb))

  console.log('变化前:', previousSnapshot)
  console.log('变化后:', currentSnapshot)

  previousSnapshot = currentSnapshot
})

结论

  • 能监听具体字段时,优先监听字段,成本最低。
  • 希望获得"整体替换"的新旧对象,使用 ref 并整体赋值。
  • 小对象可以借助 computed 生成快照。
  • 深层、复杂对象更适合 watch 加手动深拷贝。
相关推荐
深蓝AI6 小时前
Meta 开源用了八年的设计系统 astryx:连坐两天 GitHub 总榜第一,组件库开始写给 agent 的说明书
前端·人工智能
ruanno_top6 小时前
轮播每 3 秒自己翻页:键盘停不下来,读屏也不知道换了页
vue.js
IT_陈寒7 小时前
Vue的watch把我坑惨了,原来要这样用才靠谱
前端·人工智能·后端
1-1=07 小时前
Vue 3 模板语
前端·javascript·vue.js
用户7610304473717 小时前
【转战智能眼镜开发】ROKID AIUI 小游戏 亚马逊标枪项目实战讲解
前端
风卿7 小时前
看着AI写代码太孤独了,来欣赏星云吧:一个DSH 装饰插件
前端·javascript
牧艺7 小时前
弹层不用再手算坐标了:CSS Anchor Positioning 从原理到可上线
前端·javascript·css
用户老李7 小时前
YOLO 目标检测自学教程 07:YOLOv3——多尺度预测
前端
moshuying8 小时前
ESP32 墨水屏金价牌(上):从点亮到把图片推过去
前端
李剑一8 小时前
别再抬杠,前端岗已经没了!无论前后端,我们最终都要成“全干”工程师
前端·面试