在 Vue 中,为什么向对象动态添加新属性后,界面不会自动更新?
考察说明
考查对 Vue 响应式原理的理解,特别是对象属性劫持的局限。
回答思路
- 【回答框架 1】Vue 2 的响应式系统基于 Object.defineProperty 实现,通过 getter/setter 劫持属性。在组件初始化时,data 中的对象属性会被递归转换为 getter/setter,从而具备依赖收集和触发更新的能力。但是,Vue 2 只能劫持已存在的属性,对于之后新增的属性,它没有被 getter/setter 覆盖,因此不是响应式的。
- 【回答框架 2】当向对象添加新属性时,由于该属性没有 setter,Vue 无法检测到它的变化。因此,即使修改了这个新属性,也不会触发视图更新。这是 Vue 2 的机制限制。
- 【回答框架 3】Vue 3 则使用了 Proxy 代理整个对象,可以拦截属性的添加和删除,所以不存在这个问题。因此,这个问题通常特指 Vue 2 的情况。
- 【回答框架 4】在 Vue 2 中,解决这个问题有三种常见方法:使用 Vue.set(object, key, value) 方法、使用 this.$set(object, key, value)(它是 Vue.set 的实例方法),或者使用 Object.assign 创建新对象再赋值,例如 this.obj = Object.assign({}, this.obj, { newProp: value })。
- 【关键点 1】Vue 2 用 Object.defineProperty 劫持属性,只能拦截已存在的属性。
- 【关键点 2】新增属性没有 getter/setter,因此不是响应式,导致界面不更新。
- 【关键点 3】Vue 3 使用 Proxy,可以拦截新增属性,因此没有此问题。
- 【关键点 4】Vue 2 中解决方式:Vue.set、this.$set 或整体替换对象。
- 【易错点 1】不要认为 Vue.set 能处理所有情况,它只能增加响应式属性,不能处理数组下标修改。
- 【易错点 2】直接使用 this.obj.newProp = value 在 Vue 3 中可行,在 Vue 2 中无效。
- 【易错点 3】使用 Object.assign 时,如果第一个参数是空对象,会丢失原有响应式属性,应该先拷贝原对象。