vue2-给data动态添加属性
1. 问题的来源
- 在VUe2中(VUE3中使用了proxy,及时动态添加也能实现响应式),如果我们动态给data添加一个属性,会发现视图没有同步更新
- 举个例子
- 我们通过v-for遍历data中的一个属性list,生成一系列的div标签
- 然后有一个按钮,点击按钮增加一个data的属性list的子项,
- 当我们点击按钮时,会发现并没有多一个div出来
<div v-for="(item,index) in list">{{item}}</div>
<button @click="addProperty">动态添加属性</button>
<script>
const app=new Vue({
data(){
return{
list:{
name:'tom'
}
}
},
methods:{
addProperty(){
this.list.age=12
console.log(this.list)
}
}
})
</script>
- 我们点击按钮,发现打印出了新属性但是视图没有得到更新
- 为什么呢,得从VUE2的响应式原理说起
2. 原理分析
- 在VUE2中,响应式是通过Object.defineProperty来实现的
const obj={
name:'tom'
}
Object.defineProperty(obj,'name',{
get(){
console.log(`get name ${obj['name']}`)
return obj['name']
},
set(newVal){
console.log(`set name ${newVal}`)
obj['name']=newVal
}
})
- 当我们访问name(obj.name)或者设置name(obj.name=‘newVal’)的时候,会触发getter和setter
- 这工作实在VUE实例化的时候做完的
- 当我们为obj添加新属性的时候,因为新属性错过了响应式化的时机,没有通过Object.defineProperty设置为响应式数据,所以数据改变时,无法通知Watcher去更新视图
3. 解决方案
3.1 Vue.set()
- 既然是因为错过了响应化时机,那我们就再把新属性响应化一次就行
- 通过Vue.set向响应式对象中添加一个property,并确保这个新property同样是响应式的额。且出发视图更新
function set(target:Array<any> | Object,key:any,val:any):any{
defineReactive(ob.value,key,val)
ob.dep.notify()
return val
}
3.2 Object.assign()
- 直接使用Object.assign添加到对象的新属不会触发更新,需要新建一个对象,合并源对象与混入对象的属性
this.oldObject=Obejct.assign({},this.onlObject,{newProperty1:1,newProperty2:2,...})
3.3 $forceUpdate
- 看名字就知道了,强制更新,迫使Vue实例重新渲染,进影响实例本身和插入插槽内容的子组件
4. 总结
- 如果要添加少量 的新属性,用Vue.set()
- 如果要添加大量的新属性,Object.assign()
- 如果你就想然他跟新,那干脆$forceUpdate,但是建议少用