问题
不知道大家有没有遇到过这个问题,当我们给data里面声明的对象添加一个新属性,这个新属性是不会动态更新视图的。
看一个例子
<template>
<div>
{{obj}}<br>
<button @click="addProperty">动态添加新属性</button>
</div>
</template>
<script>
export default {
data(){
return {
obj:{
oldProperty:"旧值"
}
}
},
methods:{
addProperty(){
this.obj.newProperty = "新值"
console.log(this.obj)
}
}
};
</script>
在浏览器中输出的是最新结果,但是页面视图并没有更新
原因
vue2使用Object.defineProperty
实现数据响应式
组件初始化时,对data中的obj进行递归遍历,对obj的每一个属性进行劫持,添加set,get方法。我们后来新加的newProperty属性,并没有通过Object.defineProperty设置成响应式数据,所以修改后不会视图更新
解决
Vue 不允许在已经创建的实例上动态添加新的响应式属性
若想实现数据与视图同步更新,可采取下面三种解决方案:
1. Vue.set( target, key, value)
参数
target:要修改的对象或数组
key:属性或下标
value:新值
methods:{
addProperty(){
this.$set(this.obj, "newProperty", "新值");
console.log(this.obj)
}
}
我们可以看到页面已经更新了:
2. Object.assign(target, ...sources)
参数
target:目标对象
souce:源对象(可多个)
直接使用Object.assign()添加到对象的新属性不会触发更新
应创建一个新的对象,合并原对象和混入对象的属性
methods:{
addProperty(){
// this.obj = Object.assign(this.obj,{newProperty:'新值'})
// 以上写法不会更新,要生成一个新对象
this.obj = Object.assign({},this.obj,{newProperty:'新值'})
console.log(this.obj)
}
}
3. lodash
也可以使用lodash库的_assign() 和 _merge()为对象添加响应式属性,触发视图更新
this.obj = _assign({},this.obj,{newProperty:'新值'})
this.obj = _merge({},this.obj,{newProperty:'新值'})
4. $forcecUpdated()
$forceUpdate迫使Vue 实例重新渲染(不建议使用)
methods:{
addProperty(){
this.obj.newProperty = "新值"
this.$forceUpdate();
console.log(this.obj)
}
}
5. 对于数组
# 数组对象直接修改属性,可以触发视图更新
arr:[{name:'uu盘'},{name:'hhh'}]
this.arr[0].name = 'hhh'
this.arr.forEach(item=>{
item.name = 'gungun'
})
# splice方法修改数组,可以触发视图更新
arr:[{name:'uu盘'},{name:'hhh'}]
this.arr.splice(0,1)
# 数组赋值为新数组,可以触发视图更新
arr:[{name:'uu盘'},{name:'hhh'}]
this.arr = this.arr.filter(item=>item.name.includes('h'))
# Vue提供了如下的数组的变异方法,可以触发视图更新
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
<template>
<div>
{{obj}}<br>
{{arr}}
<button @click="addProperty">动态添加新属性</button>
</div>
</template>
<script>
export default {
data(){
return {
obj:{
oldProperty:"旧值"
},
arr:[{name:'uu盘'},{name:'hhh'}]
}
},
methods:{
addProperty(){
// this.arr = this.arr.filter(item=>item.name.includes('h'))
// this.arr.splice(0,1)
// this.arr[0].name = 'hhh'
this.arr.forEach(item=>{
item.name = 'gungun'
})
this.obj.newProperty = "新值"
console.log(this.obj)
}
}
};
</script>
当我们点击按钮之后,数据与视图同步更新
注意:在页面中我们要使用arr变量才能有效
总结
- 如果为对象添加少量的新属性,可以直接采用Vue.set()
- 如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
- 如果你需要进行强制刷新时,可采取$forceUpdate() (不建议)
注意:vue3是用过proxy实现数据响应式的,直接动态添加新属性仍可以实现数据响应式