if (key in target && !(key in Object.prototype)) { target[key] = val return val }
这个if条件的意思是该属性已经在target对象上有定义了,那么只要重新设置它的值就行了。因为在纯对象中,已经存在的属性就是响应式的了。
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 }
target._isVue
拥有_isVue属性说明这是一个Vue实例‘
(ob && ob.vmCount)
ob就是target.__ob__,ob.vmCount也就是target.__ob__.vmCount。来看一下这段代码:
export function observe (value: any, asRootData: ?boolean): Observer | void { if (asRootData && ob) { ob.vmCount++ } }
asRootData表示是否是根数据对象。什么是根数据对象呢?看一下哪里调用observe函数的时候传递了第二个参数:
function initData (vm: Component) { ... // observe data observe(data, true /* asRootData */) }
在initData中调用observe的时候传递了第二个参数为true,那根数据对象也就是data。也就是说当使用 Vue.set/$set 函数为根数据对象添加属性时,是不被允许的。
所以当target是Vue实例或者是根数据对象时,在非生产环境会打印警告信息。
if (!ob) { target[key] = val return val }
当!ob为true时,说明不存在__ob__属性,那target也就不是响应式的,直接变更属性值就行。
defineReactive(ob.value, key, val) ob.dep.notify()
这里就是给对象添加新的属性,并保证新添加的属性是响应式的。
ob.dep.notify()触发响应。
del
看完了set,再来看delete操作。
if (process.env.NODE_ENV !== 'production' && (isUndef(target) || isPrimitive(target)) ) { warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`) }
这个if判断跟set函数的一样。如果target是undefined、null或者原始类型值,在非生产环境下打印警告信息。
if (Array.isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1) return }
当target是数组类型并且key是有效的数组索引值时,也是使用splice来进行删除操作,因为该变异方法可以触发拦截操作。