一、set 方法的作用
-
set 方法主要作用是向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,对象不能是 Vue 实例,或者 Vue 实例的根数据对象
-
Vue.set( target, key, value ) / this.$set( target, key, value )
target:要更改的数据源(可以是对象或者数组)
key:要更改的具体数据,或者新增的属性名
value :重新赋的值
二、set 实现
首先 set 方法会对参数中的 target 进行类型判断
- 如果是 undefined 、null 、基本数据类型,直接报错。
- 如果为数组,取当前数组长度与 key 这两者的最大值作为数组的新长度,然后使用数组的 splice 方法将传入的索引 key 对应的 val 值添加进数组。target 在 observe 的时候,原型链被修改了, splice 方法也已经被重写了,触发之后会再次遍历数组,进行数据劫持,也就是说当使用 splice 方法向数组内添加元素时,该元素会自动被变成响应式的
- 如果为对象,会先判断 key 值是否存在于对象中,如果在,则直接替换 value。如果不在,就判断 target 是不是响应式对象(其实就是判断它是否有
__ob__属性),接着判断如果它是不是 Vue 实例,或者是 Vue 实例的根数据对象,如果是则抛出警告并退出程序。如果 target 不是响应式对象,就直接给 target 的 key 赋值,如果 target 是响应式对象,就调用 defineReactive 将新属性的值添加到 target 上,并进行依赖收集,更新视频
function set(target: Array<any> | Object, key: any, val: any): any {
// isUndef 是判断 target 是不是等于 undefined 或者 null 。
// isPrimitive 是判断 target 的数据类型是不是 string、number、symbol、boolean 中的一种
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
}
// vue给响应式对象(比如 data 里定义的对象)都加了一个 __ob__ 属性,
// 如果一个对象有这个 __ob__ 属性,那么就说明这个对象是响应式对象,修改对象已有属性的时候就会触发页面渲染
// 非 data 里定义的就不是响应式对象。
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
}