Vue2 对象新增属性不响应
解决 Vue2 中动态添加对象属性时视图不更新的问题
问题
在 Vue2 中给对象动态添加新属性时,数据虽然更新了,但页面不会重新渲染。
<template>
<div>
<p v-for="(value, key) in item" :key="key">
{{ value }}
</p>
<button @click="addProperty">添加属性</button>
</div>
</template>
<script>
export default {
data() {
return {
item: {
oldProperty: "旧属性"
}
}
},
methods: {
addProperty() {
this.item.newProperty = "新属性" // 数据更新了,但页面不刷新
console.log(this.item) // 能看到 newProperty
}
}
}
</script>
解答
原因分析
Vue2 使用 Object.defineProperty 实现响应式,只能拦截已存在的属性:
const obj = {}
Object.defineProperty(obj, 'foo', {
get() {
console.log(`get foo:${val}`)
return val
},
set(newVal) {
if (newVal !== val) {
console.log(`set foo:${newVal}`)
val = newVal
}
}
})
obj.foo = 'new' // 触发 setter
obj.bar = '新属性' // 不会触发任何拦截
初始化时 item.oldProperty 被设置成响应式,但后续添加的 newProperty 没有经过 Object.defineProperty 处理,因此不是响应式的。
解决方案
方案一:使用 Vue.set()
methods: {
addProperty() {
this.$set(this.item, 'newProperty', '新属性')
// 或者
Vue.set(this.item, 'newProperty', '新属性')
}
}
方案二:使用 Object.assign()
创建新对象替换原对象:
methods: {
addProperty() {
this.item = Object.assign({}, this.item, {
newProperty1: '新属性1',
newProperty2: '新属性2'
})
}
}
方案三:使用 $forceUpdate()
强制重新渲染(不推荐):
methods: {
addProperty() {
this.item.newProperty = '新属性'
this.$forceUpdate()
}
}
关键点
- Vue2 的
Object.defineProperty无法检测对象新增属性,只能拦截已存在的属性 - 添加少量属性用
Vue.set()或this.$set() - 添加多个属性用
Object.assign()创建新对象 $forceUpdate()可以强制刷新,但应该作为最后手段,通常说明代码有问题- Vue3 使用 Proxy 实现响应式,可以直接检测新增属性,不存在这个问题
目录