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,但是建议少用