Appearance
set
javascript
// 向响应式对象中添加一个 property, 并确保这个新 property 同样是响应式的,
// 且触发视图更新。
// 如果 target 是基本数据类型,直接警告。如果是数组并且索引合法,通过 splice 添加,
// 添加完后会更新视图。否则判断对象自身中是否有该属性,如果有该属性直接重写即可,
// 此时如果该对象是响应式的就出发视图更新,如果不是响应式的也不管了。
// 如果没有该属性说明是往对象中添加属性,先获取 target 的 ob,如果 target._isVue
// 为真或 ob.vmCount 为真说明要操作的对象是 vm 或 data,直接警告。
// ob 是否为假,为假说明这个对象不是响应式的,直接添加属性即可。
// 否则说明对象是响应式的,通过 defineReactive 为对象定义一个响应式属性。
// 最后调用 ob.dep.notify() 更新视图。
Vue.set = function set (target, key, val) {
// Vue.set(obj.a, 'b', 'c')
if (isUndef(target) || isPrimitive(target)) {
// warn
}
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val) // splice是变更方法
return val
}
if (key in target && !(key in Object.prototype)) { // target.hasOwnProperty(key)
target[key] = val // target上已经有key属性了, 直接重写即可
return val
}
var ob = (target).__ob__; // 获取obj.a.__ob__
if (target._isVue || (ob && ob.vmCount)) { // target是Vue实例或vm._data
// warn
return val
}
if (!ob) {
// target不是响应式的, 直接添加属性
target[key] = val;
return val
}
// 设置响应式
defineReactive(ob.value, key, val) // Observer构造器中this.value = value
// 更新视图
ob.dep.notify()
return val
}
function defineReactive(
obj: object,
key: string,
val?: any
) {
// 每个属性收集使用它们的 watcher
const dep = new Dep()
// 需要实现数组或对象所有层次的对象的属性的劫持
// childOb 是 Observe 实例, val 是数组或对象时 childOb 才有值
let childOb = observe(val, false, mock)
// 为该属性实现响应式
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = val
// 如果模板外使用数据,不进行收集操作。
if (Dep.target) {
// 互相收集
dep.depend()
// value 是数组或对象
if (childOb) {
// 数组变更方法响应式 & Vue.set() 所有层次属性的响应式
childOb.dep.depend()
// 数组嵌套时内部数组变更方法响应式
if (isArray(value)) {
dependArray(value)
}
}
}
}
})
}