Vue源码阅读——MVVM原理Observe、watcher、dep

Vue源码阅读——MVVM原理Observe、watcher、dep

  • 一直在使用Vue,但是却是不知道双向绑定的具体原理Vue是怎么实现的,比如只知道Object.defineProperty();
  • 在实习的项目中有时候会遇到的问题就是,从后台获取到的数据res存到data里面,然而你需要改变res里面存在的数据比如res里面有一个items对象数组,你为数组里面的一个对象添加之前不存在的字段的时候,视图并不会实时更新;因为Vue并没有通过render去更新DOM树的内容;
  • 在vue的文档里面指出,vue的双向绑定只会响应data里面有的数据字段,以及子元素;动态添加的元素需要使用Vue.$set(obj, key, value)来添加;

在Vue中,MVVM的实现是在src/core/observer/index watcher dep中,这是主要的核心代码,index定义了Observer,每个Observer初始化的时候都会创建一个收集器Dep,然后通过walk()初始化,walk的调用是对函数defineReactive()

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// 循环遍历data,将data中的属性变成响应属性
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
// Object.defineProperty定义每一个属性
defineReactive(obj, keys[i], obj[keys[i]])
}
}
// defineReactive function
const dep = new Dep()
// 获取key在obj的属性描述符,之后获取getter,setter
const property = Object.getOwnPropertyDescriptor(obj, key)
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
// objserve对应的val
let childOb = !shallow && observe(val)
// 将data中
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// 定义getter
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
// 收集依赖
dep.depend()
if (childOb) {
childOb.dep.depend()
}
// 如果属性为数组,添加数组的依赖收集
if (Array.isArray(value)) {
dependArray(value)
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
// 设置新的val,触发setter
val = newVal
}
childOb = !shallow && observe(newVal)
// 通知dep收集器数据有变化
dep.notify()
}
})
// dep.js
// 循环遍历收集器更新变化,每一个sub都是一个watcher,在watcher中,get,update,run()函数综合了更新流程;
// 收集器subs[i].update()就是通知watcher更新update()。update中通知run。
// run会调用get函数,get函数会将当前的对象(watcher)添加到dep的一个全局属性target数组中,
// 确保始终有一个观察者是可用的,或者说是存在的;一般情况下是唯一一个;watcher在执行get的时候,
// 通过value = this.getter.call(vm, vm)获取元素触发getter;让observer,dep,watcher联系在一起
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
//

思维导图:

参考:

坚持原创技术分享,您的支持将鼓励我继续创作!