前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库Vue deep watch oldValue newValue 相同
VuVue侦听与生命周期

Vue 深度 watch 的 oldValue 和 newValue 为什么是同一个对象?

深监听检测嵌套变化,但不替你保存整棵对象的历史副本;同一对象被修改后,新旧引用仍可能相同。

前端进阶之旅 · 一题精讲更新于 2026.09.06
Vue#侦听与生命周期
先看核心答案读代码示例
理解线索

先区分三种“旧”的含义

  1. 旧引用上一轮监听保存的对象入口
  2. 旧内容修改发生前那个对象各字段的值
  3. 业务历史可用于撤销、审计或差异提交的明确记录

对回调里已经变化的 oldValue 再深拷贝,只能复制现在看到的内容,不能恢复之前的历史。

核心回答

先记住这个答案

对象内部字段改变时,外层对象没有被替换。深度 watch 能因嵌套变化触发回调,但 newValue 与 oldValue 可能仍指向同一响应式对象,所以两边读取字段时都看到修改后的内容。这不代表监听没生效,而是引用不等于历史快照。只关心少量字段时可以监听返回原始值的 getter;需要完整差异时,应提前维护适合数据类型的快照或记录明确的业务更新动作。

  • 回调触发与对象引用是否替换是两个问题
  • oldValue 不是深度历史快照的保证
  • 想得到旧字段值,应在它被覆盖前保存或监听该值

同一个对象变化后,两条引用看到同一份内容

假设 state.preferences.level 从一变成二,state 仍是原来的代理对象。深监听回调中的两个参数都指向它,于是 oldValue.preferences.level 也读取到二。Vue 已经观察到了变化,否则回调不会触发,但它没有为每个监听器自动保存修改前的整棵树。

下面同时监听整个对象和一个返回原始数值的 getter。对象回调显示引用相同、两边字段都为二;字段回调则有二和一这两个独立数值。示例使用同步 flush 方便观察一条赋值的结果,业务中不应为了得到日志顺序而对高频大对象一律使用同步回调。

对象引用与原始字段旧值对照TypeScript
import { reactive, watch } from 'vue';

const state = reactive({ preferences: { level: 1 } });
const objects: Array<{ same: boolean; before: number; after: number }> = [];
const fields: number[][] = [];
const stopObject = watch(state, (next, previous) => {
  objects.push({ same: next === previous, before: previous.preferences.level, after: next.preferences.level });
}, { flush: 'sync' });
const stopField = watch(() => state.preferences.level, (next, previous) => {
  fields.push([next, previous]);
}, { flush: 'sync' });

state.preferences.level = 2;
stopObject();
stopField();

对象记录为 same=true、before=2、after=2,字段记录为 [2, 1]。两种监听都触发了,只是保存的来源结果不同;示例没有固定控制台输出。

需要完整差异时,快照必须有正确时机

如果要提交修改差异,可以先建立一份已确认版本快照,再在变更后比较当前值,并按业务规则推进基准版本。快照应在旧内容尚存在时生成,回调里第一次看到 oldValue 才开始复制,往往已经太晚。保存成功后是否更新基准,还要考虑同时发生的新编辑。

复制方式也依赖数据类型。JSON 方式不能完整保留所有类型,原始响应式代理也不能直接假定适用于每一种克隆 API。应定义可序列化的数据边界与复制规则,避免把函数、外部实例或循环关系混进简单表单快照,然后以克隆失败为由放弃历史一致性。

撤销与审计不一定需要每次深拷贝

复杂编辑器可以记录语义化动作、不可变版本或增量补丁,而不是每次按键都复制全部状态。这些方案需要明确动作是否可逆、如何合并连续操作,以及外部数据更新与本地草稿的冲突处理。watch 的新旧参数不能自动提供这些业务语义。

验收时可以分别测试嵌套修改和整个对象替换,确认差异逻辑是否处理两种情况。默认调度可能合并同一轮同步变化,所以回调也不等于每个底层赋值的完整事件日志。需要逐动作审计时,应在明确的更新入口记录,而不是依赖观察器猜测用户做了什么。

回答前,多想一步

容易答错的地方

newValue 等于 oldValue 就说明这次回调没有意义
引用相同仍可能包含已经发生的嵌套变化。需要根据监听目标决定回调职责,而不是把对象身份比较当作所有内容变化的判断方式。
在回调里 clone(oldValue) 就能拿到修改前状态
如果旧参数指向已被修改的同一个对象,克隆得到的也是当前内容。历史数据必须在覆盖前保存,或者通过明确的不可变更新与动作日志保留。
试着用自己的话回答

面试官还会怎么问?

整个对象替换后,新旧参数还会相同吗?

通过能观察替换的来源,例如 ref 或返回对象属性的 getter,替换为新对象时通常可以得到不同引用。直接监听旧对象的入口则不等于持续跟随外层变量替换。

监听 getter 返回一个新数组能获得字段旧值吗?

如果数组项是读取到的原始值,可以保留这些值在上一轮的组合结果。但嵌套对象仍可能共享引用,不能把外层新数组当成自动深拷贝。

深监听适合精确记录每次赋值吗?

默认调度可能合并同步变化,且回调没有天然的业务动作含义。需要逐操作历史时,使用明确的更新入口和记录协议更可靠。

从一道题,走向一组知识

把知识连起来

侦听与生命周期

Vue watch 开启 deep 会发生什么,为什么可能增加性能成本?

深度遍历决定能观察哪些变化,但并不负责保存历史内容。

侦听与生命周期

Vue watch 怎样监听多个来源,新旧参数数组如何对应?

少量字段的前后值可以用明确的多个来源表达,减少不必要的整体快照。

参考资料

  • Watchers: Deep Watchers
  • Reactivity API: watch

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 同一个对象变化后,两条引用看到同一份内容
  3. 需要完整差异时,快照必须有正确时机
  4. 撤销与审计不一定需要每次深拷贝
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑