状态更新,视图自动更新主要靠依赖收集。数据劫持 getter 和 setter,结合发布订阅模式,在 getter 中收集依赖,setter 中触发更新机制,重新渲染视图。
依赖收集有两个重要组成,Dep 依赖管理器和 Watcher 依赖,他们是多对多关系,一个 dep 有多个 watcher,一个 watcher 有多个 dep。每个属性都有 dep,每个对象或数组本身也有 dep,当取值时走 get,在 get 中调用 dep.depend()收集依赖,并且过滤重复 watcher,同时也在 watcher 中收集对应的 dep。更改状态走 set,在 set 中调用 dep.notify()触发依赖异步批量更新。对应数组,同样在 get 中收集依赖,但是触发依赖更新在重写的方法中。
observer/index.js
export function defineReactive(obj, key, value) {
let dep = new Dep(); //dep是为key服务的
let childOb = observe(value); //递归拦截数据
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend(); //让对象本身或者数组本身进行依赖收集
//数组中是对象,会在JSON.stringify的作用下对对象每个属性进行取值,收集依赖(对象的属性),所以不用处理数组中对象的情况
//数组中嵌套数组,需要对内部的数组进行依赖收集处理
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value;
},
set(newVal) {
if (newVal === value) return;
childOb = observe(newVal); // 对set的数据拦截
value = newVal;
dep.notify();
}
});
}
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
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
dep.js
/** 依赖管理器dep
* 每个属性,每个对象(数组)都有dep实例,用来收集watcher
* 每个watcher都会收集dep,所以dep和watcher是多对多的关系;一个属性用在多个组件中,一个组件有多个属性
*/
let id = 0;
class Dep {
constructor() {
this.id = id++;
this.subs = [];
}
depend() {
Dep.target.addDep(this);
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
Dep.target = null;
export default Dep;
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
watcher.js
import Dep from './dep';
import { queueWatcher } from './scheduler';
let id = 0;
/**
* exprOrFn是更新逻辑 :
* 如果是函数,就是渲染watcher,propOrCb == updateComponent()
* 如果是字符串,就是用户watcher,propOrCb == 属性名
* callback是用户watcher的回调
* 渲染watcher 计算属性watcher watch用户watcher
*/
class Watcher {
constructor(vm, exprOrFn, callback, options = {}) {
this.id = id++;
this.deps = [];
this.depIds = new Set();
this.callback = callback;
this.options = options;
//执行取值会收集watcher
this.getter =
typeof exprOrFn === 'function' ? exprOrFn : () => vm[exprOrFn];
this.oldValue = this.get(); //默认先执行一次,获取老值
}
get() {
//执行之前先把当 前的watcher放在全局上,取值走get的时候可以拿到watcher,执行完毕后再将全局上置空
Dep.target = this;
let value = this.getter();
Dep.target = null;
return value;
}
update() {
queueWatcher(this);
}
addDep(dep) {
if (!this.depIds.has(dep.id)) {
this.depIds.add(dep.id);
this.deps.push(dep);
dep.addSub(this);
}
}
//真正的执行更新
run() {
let newValue = this.get();
//用户watcher才会进
if (this.options.user) {
this.callback(newValue, this.oldValue);
}
}
}
export default Watcher;
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
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