Vue3 响应式系统中 Reflect.set 的更新失效问题:为什么直接返回 Reflect.set 会导致更新错误?
在 Vue3 的响应式系统中,Reflect.set 被用于设置对象的属性值,从而触发依赖的更新。然而,如果在自行实现响应式逻辑时,直接返回 Reflect.set 的执行结果,可能会在某些情况下导致更新异常。

问题原因
当在 set 拦截器中直接返回 Reflect.set(...arguments) 时,某些场景下会出现问题。例如,在 setTimeout 回调中连续更新同一对象的两个属性,可能会导致其中一个属性的更新未能正确触发响应。这背后涉及依赖收集与更新触发的时机问题:
- 首次执行 Reflect.set 时,会收集依赖,但并未立即触发更新;
- 紧接着再次收集依赖时,收集过程会基于对象当前属性值进行;
- 如果直接返回 Reflect.set 的结果,此时对象的属性值可能尚未更新,导致第二次收集的依赖关系不准确,从而影响后续更新触发。
解决方案:先执行赋值,再触发更新
为了保证依赖在正确的状态下被收集,可以在执行 Reflect.set 之后,先将其结果暂存,手动触发更新逻辑,再返回结果。这样能够确保在触发更新时,对象属性已处于最新状态,依赖收集也能基于正确的值进行。
下面是优化后的 set 拦截器示例代码:
// 响应式对象的 set 拦截器实现
set(target, prop, value, receiver) {
// 先执行 Reflect.set 完成属性赋值,暂存执行结果
const result = Reflect.set(target, prop, value, receiver);
// 手动触发对应属性的更新逻辑,此时属性已经是新值,依赖收集准确
trigger(target, prop);
// 返回之前的执行结果
return result;
}通过这种方式,在类似 setTimeout 中连续更新对象多个属性的场景下,每个属性的变更都能正确触发依赖更新,从而确保响应式系统按预期工作。