Vue.set 是什么?
set 的三个参数:target、key、value
{Object | Array} target数据源对象或者是数组{string | number} key数据源新增的属性或者是替换数组项下标{any} value新增或修改的值
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')
new Vue({
data() {
return {
person: {
name: "张三",
},
};
},
created() {
// 普通的新增属性并不会具备响应式
this.person.age = 18;
// this.$set(this.person, 'age', 18)
},
}).$mount("#app");
Vue.set 的实现原理
源码目录:
/src/core/observer/index
/**
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
*
* 设置对象的属性。 添加新属性并在该属性尚不存在时触发更改通知。
*/
export function set(target: Array<any> | Object, key: any, val: any): any {
/*
process.env.NODE_ENV:判断当前开发环境
isUndef: 判断数据源是否不存在
isPrimitive:判断是否是基础数据类型
*/
if (
process.env.NODE_ENV !== "production" &&
(isUndef(target) || isPrimitive(target))
) {
warn(
`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
);
}
/*
isValidArrayIndex:判断key是否是一个索引
*/
if (Array.isArray(target) && isValidArrayIndex(key)) {
/*
Math.max 取数据中最大那个那个值
扩展:
Math.max以及Math.min一个取最大值,一个取最小值,可用于数组
let arr = [1,3,5,6,2,4,8,10,9,4]
Math.max.apply(Math,arr)
Math.min.apply(Math,arr)
*/
target.length = Math.max(target.length, key);
/*
根据key的下标直接替换val,如果key超出target长度,进行扩展的同时
也会添加相对应的空位
*/
target.splice(key, 1, val);
return val;
}
// 判断key在数据源里面并且不是 Object.prototype 的属性
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
// ob 是一个观察者对象
const ob = (target: any).__ob__;
// 数据源不能是Vue实列
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== "production" &&
warn(
"[避免向 Vue 实例或其根 $data 添加响应式属性]Avoid adding reactive properties to a Vue instance or its root $data " +
"at runtime - declare it upfront in the data option."
);
return val;
}
// 观察者对象不存在表示是一个普通的对象
if (!ob) {
target[key] = val;
return val;
}
// 让新增的属性具备响应式
defineReactive(ob.value, key, val);
// 通知属性改变
ob.dep.notify();
return val;
}
总结
Vue.set 会优先判断数据源target是否存在(不为 null 或者 undefined)并且需要是一个复杂数据类型,然后对target是否是数组或者对象分开判断
-
数组
Array判断
target是否是数组并且 key 是否是索引,然后直接调用 splice 方法进行替换或赋值,需要注意的是,如果 key 索引大于target的长度,那么除了target的长度会直接等于 key 以外,数组中也会添加相对应的空位 -
对象
Object- key 存在
如果 key 在
target中存在并且不是一个 Object 的属性关键字,那么就会直接赋值 - key 不存在
利用
Object.defineProperty数据双向劫持让新增的属性具备响应式
- key 存在