Vue2.x之$set是怎么实现的
我们都知道 Vue 通过 defineProperty 对 data 内的属性进行监听,但是监听不到对象属性。那么 Vue 是怎么用 vm.$set() 解决对象新增属性不能响应的问题呢?让我们看看~
# Vue 为什么要用$set()解决对象新增属性不能响应的问题
Vue 使用了 Object.defineProperty 实现双向数据绑定,在初始化实例时对属性执行 getter/setter 转化,是对 data 内的属性进行循环监听,这就会导致对象属性,这类具有深层次的类型无法被监听。我的理解是可以看作它监听到的是对这个对象的引用的地址,只有地址发生了改变才能被监听的到。出于解决这个问题,Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
# $set 源码的实现
源码位置:vue/src/core/instance/index.js
export function set(target: Array<any> | Object, key: any, val: any): any {
// target 为数组
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
target.length = Math.max(target.length, key);
// 利用数组的splice变异方法触发响应式(splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。)
target.splice(key, 1, val);
return val;
}
// key 已经存在,直接修改属性值
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
const ob = (target: any).__ob__;
// target 本身就不是响应式数据, 直接赋值
if (!ob) {
target[key] = val;
return val;
}
// 对属性进行响应式处理
defineReactive(ob.value, key, val);
ob.dep.notify();
return val;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
我们阅读以上源码可知,vm.$set 的实现原理是:
- 如果目标是数组,直接使用数组的 splice 方法触发响应
- 如果目标是对象,会先判读属性是否存在、对象是否是响应
- 最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理
- defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法,详情可以看 function Observer 构造函数。setProxy
编辑 (opens new window)
上次更新: 2023/03/27, 21:41:56