Appearance
25.Vue.set 方法是如何实现的?
Vue2
不允许在已经创建的实例上动态添加新的响应式属性。所以采用 set API 来进行实现。
typescript
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;
}
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;
}
当我们选择新增属性时,可以考虑使用对象合并的方式实现
javascript
this.info = {...this.info,...{newProperty1:1,newProperty2:2 ...}}
this.info = {...this.info,...{newProperty1:1,newProperty2:2 ...}}
Vue3
则采用 proxy 来进行数据劫持,可以直接劫持到属性新增的逻辑,无需采用补丁的方式来进行实现。