vue中$set用法及其源码的底层原理
在我们开发过程中,经常会遇到,为一个数组或者对象data中添加一个属性,点击按钮后发现,控制台打印明明对象中已经出现了这个属性,视图层却并没有更新该数据,这是因为受到JS的限制,vue.js中不能监听对象属性的添加和删除,因为在vue组件初始化过程中,会调用getter和setter方法,所以该属性只有存在data中时,试图层才会响应数据的变化。
当我们对对象属性进行添加和删除的操作时,或者数组通过索引去修改值时都不会触发vue的响应式原理,导致视图并不会渲染,数据也不会并不会改变,这个时候就要使用到$set
解决办法:
(1)通过
s
e
t
对
象
中
:
t
h
i
s
.
set 对象中:this.
set对象中:this.set(obj,key,value)
student: {
name:'张三',
age:16
}
点击事件中:
changeStudent() {
this.$set(this.student, 'name', '陈皮阿四')
}
console.log(this.student) //{name:'陈皮阿四',age:16}
数组中:this.$set(obj,index,value)
student: [1,2,3,'chen']
点击事件中:
changeStudent() {
this.$set(this.student, 1, 'chenchen')
}
console.log(this.student) //['chenchen',2,3,'chen']
(2)通过Object.assign(target, sources)方法
student: {
name:'张三',
age:16
}
点击事件中:
changeStudent() {
this.student.name='陈皮阿四'
this.student = Object.assign({}, this.student)
console.log(this.student) // {name:'陈皮阿四',age:16}
}
我们通过这两种方法为对象添加属性之后,他的对象上就多了set和get方法,再次操作属性的时候,就会引起视图的更新了
$set的源码分析
通过传入target,key,value三个值,首先通过isArray判断target是否是数组,是的话则比较key与当前数组
长度的最大值作为数组新长度,然后通过数组splice方法将传入key对应的val值添加进新数组;如果传入的不是
数组则被当做对象处理,通过in方法判断key是否存在target中,如果存在则只是普通的修改值,如果不存在则
说明是对对象属性值的添加或者删除,我们此时要去判断target的__ob__属性,如果为false则说明不是响应式,
只需要简单为他添加上新的属性,如果为true的话则说明是响应式,我们就需要通过defineReactive(将新属
性添加完后转为响应式)将新熟悉添加到target上,最后通知依赖进行更新
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
//如果传入的对象是一个数组且索引是一个正常索引
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
//如果值在对象中
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val) //加入依赖收集
ob.dep.notify() //通知更新
return val
}