Vue2 响应式原理是什么?

先理解原理和示例,再练习完整口述与追问。

对被观察对象的已有属性使用 Object.defineProperty 定义 getter / setter。读取时收集依赖,写入时通知相关 watcher,再经调度更新组件。数组通过对变更方法的处理触发通知,不是所有索引都被逐一拦截。

Vue2 对初始化数据做观察,把已有可配置属性转换成 getter/setter;每个属性关联依赖容器 Dep。组件首次渲染时,渲染 watcher 成为当前收集目标,模板读取字段会经过 getter,把该 watcher 登记为依赖。setter 收到变化后通知依赖,watcher 进入去重队列,稍后重新运行渲染函数并 patch。对象的嵌套属性会被继续观察,数组则主要通过改写变更方法并观察插入项来通知。

链路可以逐步推演:模板显示 user.name → 首次读取收集该组件 → 执行 user.name = 新值 → setter 通知 → 本轮同步代码继续 → 更新队列执行 → 新 VNode 与旧 VNode 比较 → 更新文本节点。响应式决定哪些计算应重跑,diff 决定重跑后如何更新 DOM,二者职责不同。未被该渲染读取的独立属性变化,不必让这个组件因此重新渲染。

不要说“数据一变就同步改 DOM”,也不要把 Dep、watcher、虚拟 DOM 当作同一个对象。新增属性和数组下标直接赋值的限制,需要结合 Vue2 实际观察方式解释,而不是泛称 JS 监听不了。

Vue2 会在初始化时遍历需要观察的对象,用 Object.defineProperty 把已有属性变成 getter 和 setter。组件渲染时读取属性,getter 把当前渲染 watcher 收集为依赖;属性赋新值时,setter 通知依赖,再通过更新队列批量重新渲染。嵌套对象会继续被观察,数组则对 push、splice 等变更方法做了处理。所以不是修改任何对象都能自动更新,前提是这次读取和修改进入了 Vue 的响应式链路。

面试官可能继续问追问与完整解答展开收起

Observer、Dep、Watcher 分别负责什么?条件分支改变后,不再使用的依赖如何清理?

Observer 负责把对象和数组转换为可观察数据;Dep 保存并通知依赖;Watcher 执行渲染、计算或监听逻辑并记录依赖。Watcher 每次执行会重新收集本轮访问到的依赖,并移除上轮有、本轮没有的订阅,因此条件分支切换后不会永久订阅所有旧字段。