给对象或者数组添加一个新的属性。如果对象或者数组是一个响应式数据,那么,新添加的属性也将会变成响应式数据,并且触发视图更新。
1、如果target对象为原始类型或者undefined、null,则警告报错
2、如果是数组,并且下标索引是有效的合法值,就会取原数组的长度和传入的下标索引值的最大值作为数组的长度,然后调用splice方法添加数据,因为splice方法被重写了,会把元素自动转化为响应式,并通知更新。返回val值
3、如果是对象,并且key值已经在目标对象上面了,直接赋值即可,并返回
4、如果key值不在目标对象上面,先判断目标对象是否为 vue 实例或者 vue 实例的根数据独享,是,则报错,退出程序;然后在判断目标对象上面的__ob__属性是否存在,不存在,说明目标对象并不是一个响应式数据,直接赋值,并退出程序。最后通过调用defineReactive函数添加新属性,并转化为响应式数据,通知更新,退出程序
注意:为什么数组不需要判断__ob__属性是否存在?因为如果是数组是一个响应式数据,那么,调用splice方法是重写过后的,如果不是响应式数据,那么,splice方法是原生的数组方法
源码位于src/core/observer/index.js,第208行
export function set (target: Array<any> | Object, key: any, val: any): any {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
// target为原始类型或者undefined、null,报错
warn(`Cannot set reactive property on undefined, null, or primitive value: ${target}`)
}
// isValidArrayIndex(key)判断索引是否为有效值
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 数组的情况
// 对比2个索引的,取最大的索引作为数组的长度
target.length = Math.max(target.length, key)
// 使用splice方法添加数据,因为splice方法被重写了,会把元素自动转化为响应式
target.splice(key, 1, val)
return val
}
// 不是数组就当做对象处理
if (key in target && !(key in Object.prototype)) {
// 如果key已经在target上了,直接赋值即可
target[key] = val
return val
}
const ob = target.__ob__
if (target._isVue || (ob && ob.vmCount)) {
// 如果target是vue实例或者vue实例的根数据独享,则报错,退出程序
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) {
// ob不存在,说明target不是一个响应式数据
// 直接赋值即可
target[key] = val
return val
}
// 调用defineReactive方法添加新属性,并转化为响应式数据
defineReactive(ob.value, key, val)
// 通知更新
ob.dep.notify()
return val
}
删除对象或者数组中的属性。如果对象或者数组是一个响应式的数据,会触发视图的更新
1、如果target对象为原始类型或者undefined、null,则警告报错
2、如果目标对象是数组,并且下标索引值为有效的,则调用splice方法进行删除
3、如果目标对象是 vue 实例或者是 vue 实例的根数据,则退出,并报错提示
4、如果 key 本来就不存在与目标对象中,则退出函数,什么都不用干
5、使用delete关键字删除属性值,
6、通知更新
源码位于src/core/observer/index.js,第256行
export function del (target: Array<any> | Object, key: any) {
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
// target不存在或者是原始值的情况下,报错
warn(`Cannot delete reactive property on undefined, null, or primitive value: ${target}`)
}
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 有效key则使用splice进行删除
target.splice(key, 1)
return
}
const ob = target.__ob__
if (target._isVue || (ob && ob.vmCount)) {
// 不能是vue实例或者是vue实例的根数据
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
if (!hasOwn(target, key)) {
// 本来就不存在于target对象中的,就不用删除
return
}
// 删除属性值
delete target[key]
if (!ob) {
return
}
// 如果是一个响应式的情况下,通知更新
ob.dep.notify()
}