状态更新,视图自动更新主要靠依赖收集。数据劫持 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

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

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