Vue2
不允许在已经创建的实例上动态添加新的响应式属性。所以采用 set API 来进行实现。
export function set(targetx, key, val) {
// 1.是开发环境 target 没定义或者是基础类型则报错
if (process.env.NODE_ENV !== "production" && (isUndef(target) || isPrimitive(target))) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${target}`)
}
// 2.如果是数组 Vue.set(array,1,100); 调用我们重写的splice方法 (这样可以更新视图)
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
// 3.如果是对象本身的属性,则直接添加即可
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
// 4.如果是Vue实例 或 根数据data时 报错,(更新_data 无意义)
const ob = target.__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
}
// 5.如果不是响应式的也不需要将其定义成响应式属性
if (!ob) {
target[key] = val
return val
}
// 6.将属性定义成响应式的
defineReactive(ob.value, key, val)
// 通知视图更新
ob.dep.notify()
return val
}
当我们选择新增属性时,可以考虑使用对象合并的方式实现
this.info = {...this.info,...{newProperty1:1,newProperty2:2 ...}}
Vue3
则采用 proxy 来进行数据劫持,可以直接劫持到属性新增的逻辑,无需采用补丁的方式来进行实现。