export function set (target: Array<any> | Object, key: any, val: any): any {
// target 非法校验,只能为数组和对象这类引用类型添加新的索引值
if (process.env.NODE_ENV !== 'production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}
// 如果 target 是数组,且索引是有效的索引值(非负整数)
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 修改数组的长度,如有必要
target.length = Math.max(target.length, key)
// 通过 splice 来添加值,因为如果 target 本身已经具备响应式,则 splice 已被挟持过了,所以自然而然新值就会变成响应式的值
target.splice(key, 1, val)
return val
}
// https://github.com/vuejs/vue/issues/6845#issuecomment-407390645
// 判断 key 是否已存在于 target 及其原型上,且不在 Object 的原型上,则仅赋值
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
// 以下的情况则是,非数组,key 值确实之前缺省
// 取出 Observer
const ob = (target: any).__ob__
// 如果 target 是 vue 实例,或 者target 是组件的根 data, 则提示非法报错
// 之所以不能在根 data 上,因为根 data 无法收集依赖
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 非响应式的,则直接赋值
if (!ob) {
target[key] = val
return val
}
// target 已经被监听过了,则使 key 值在其中具备响应式
defineReactive(ob.value, key, val)
// 通知依赖项更新
ob.dep.notify()
return val
}