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 加手动深拷贝。
相关推荐
世界一刘先生37 分钟前
不引入 JSZip,自己写一个 ZIP 打包器:CRC32、中央目录与 UTF-8 文件名
前端
全栈项目管理程序猿37 分钟前
ArcGIS JS 基础教程(34):体元系列 - 等值面 VoxelIsosurface
前端·javascript
代码搬运媛39 分钟前
如何使用 @codemirror/view 库创建一个简单的代码编辑器
前端
huali42 分钟前
草稿该存在哪:per-session 是怎么成为共识的
前端·产品经理·产品
IPIPD43 分钟前
Proxifier规则为什么没有生效?从连接日志定位规则命中与DNS路径
前端
baiyunfan44 分钟前
微信小程序做 PDA 扫码出入库:库存并发控制与 4 个坑
前端
ZoeLandia44 分钟前
React6:组件通信
前端·react.js·typescript
爱勇宝44 分钟前
AI 都这么强了,咱们程序员以后还能干点啥?
前端·后端·程序员
FPGA小徐1 小时前
【一生一芯 / PA】AM 核心文件逐行解析(上):从 start.S 到 gpu.c
前端·算法