响应式对象 getter 相关逻辑就是做依赖收集 首先定义了一个响应式对象,拥有 getter 进行读取数据的监听,在 getter 中会收集当前计算的 Watcher,然后把 Watcher 作为一个订阅者,在数据更改的时候会触发 setter。在 getter 之前会初始化一个 Dep 实例,这个 dep 实例会做依赖收集,Dep 实例是对 Watcher 实例的管理,Wacher 实例定义了…

响应式对象 getter 相关逻辑就是做依赖收集

首先定义了一个响应式对象,拥有 getter 进行读取数据的监听,在 getter 中会收集当前计算的 Watcher,然后把 Watcher 作为一个订阅者,在数据更改的时候会触发 setter。在 getter 之前会初始化一个 Dep 实例,这个 dep 实例会做依赖收集,Dep 实例是对 Watcher 实例的管理,Wacher 实例定义了 Dep 相关的属性与方法,比如增加依赖和清除依赖。new Vue 后 mountComponent,实例化一个渲染 watcher,进入 watcher 构造函数,会执行 vm.updateComponent,然后是在 vm._render 的过程中触发所有数据的 getter,Watcher 实例同时也定义了两个数组,分别是上一次添加的 Dep 实例数组和新添加的 Dep 实例数组,数组都有各自对应的 id,目的是为了每次添加完新的订阅,会移除旧的订阅,达到节约资源的目的。

依赖收集就是订阅数据变化的 watcher 收集,依赖收集的目的是为了当这些响应式数据发生变化,触发他们的 setter 的时候,能知道应该通知哪些订阅者去做响应的处理

代码块

  1. defineReactive

    定义了一个响应式对象,给对象动态添加 getter 和 setter

// src/core/observer/index.js
//
export function defineReactive(obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean) {
  // 实例化一个Dep实例
  const dep = new Dep();

  const property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
    return;
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get;
  const setter = property && property.set;
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key];
  }

  let childOb = !shallow && observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val;
      // 依赖收集的过程
      // Dep是一个类,依赖收集的值,建立数据与Wachter的桥梁
      // Dep.target是一个全局的Watcher
      if (Dep.target) {
        // 是一个Dep实例
        // 通过dep.depend做依赖收集
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value;
    }
    // ...
  });
}
  1. Dep

    Dep 是对 watcher 的一种管理

// src/core/observer/dep.js
import type Watcher from './watcher';
import { remove } from '../util/index';

let uid = 0;

export default class Dep {
  // 静态属性target,是全局唯一一个Watcher
  // 同一时间智能有一个全局Watcher被计算
  static target: ?Watcher;
  id: number;
  // 自身属性subs也是Watcher的数组
  subs: Array<Watcher>;

  constructor() {
    this.id = uid++;
    this.subs = [];
  }

  addSub(sub: Watcher) {
    this.subs.push(sub);
  }

  removeSub(sub: Watcher) {
    remove(this.subs, sub);
  }

  depend() {
    if (Dep.target) {
      // addDep在Wachter中定义的
      Dep.target.addDep(this);
    }
  }

  notify() {
    // stabilize the subscriber list first
    const subs = this.subs.slice();
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
  }
}
// the current target watcher being evaluated.
// this is globally unique because there could be only one
// watcher being evaluated at any time.
Dep.target = null;
const targetStack = [];

export function pushTarget(_target: ?Watcher) {
  if (Dep.target) targetStack.push(Dep.target);
  Dep.target = _target;
}

export function popTarget() {
  Dep.target = targetStack.pop();
}
  1. Watcher

    定义了一些和 Dep 相关的属性:Dep 实例数组,实例数组 id,定义了一些原型方法

    在 vm._render()过程中会触发所有数据的 getter

// src/core/observer/watcher.js
let uid = 0;
export default class Watcher {
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deep: boolean;
  user: boolean;
  computed: boolean;
  sync: boolean;
  dirty: boolean;
  active: boolean;
  dep: Dep;
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: SimpleSet;
  newDepIds: SimpleSet;
  before: ?Function;
  getter: Function;
  value: any;

  constructor(vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean) {
    this.vm = vm;
    if (isRenderWatcher) {
      vm._watcher = this;
    }
    vm._watchers.push(this);
    // options
    if (options) {
      this.deep = !!options.deep;
      this.user = !!options.user;
      this.computed = !!options.computed;
      this.sync = !!options.sync;
      this.before = options.before;
    } else {
      this.deep = this.user = this.computed = this.sync = false;
    }
    // 定义了一些和Dep相关的属性
    this.cb = cb;
    this.id = ++uid; // uid for batching
    this.active = true;
    this.dirty = this.computed; // for computed watchers
    // 属性1,Dep实例数组
    this.deps = [];
    // 属性2,Dep实例数组
    this.newDeps = [];
    // 属性3,Dep实例数组id
    this.depIds = new Set();
    // 属性4,Dep实例数组
    this.newDepIds = new Set();
    this.expression = process.env.NODE_ENV !== 'production' ? expOrFn.toString() : '';
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn;
    } else {
      this.getter = parsePath(expOrFn);
      if (!this.getter) {
        this.getter = function() {};
        process.env.NODE_ENV !== 'production' && warn(`Failed watching path: "${expOrFn}" ` + 'Watcher only accepts simple dot-delimited paths. ' + 'For full control, use a function instead.', vm);
      }
    }
    if (this.computed) {
      this.value = undefined;
      this.dep = new Dep();
    } else {
      this.value = this.get();
    }
  }

  // 原型方法1
  get() {
    // pushTarget定义
    // 对Dep.target赋值为当前的渲染watcher并压栈
    // export function pushTarget(_target: ?Watcher) {
    //   if (Dep.target) targetStack.push(Dep.target);
    //   Dep.target = _target;
    // }
    pushTarget(this);
    let value;
    const vm = this.vm;
    try {
      // this.getter就是updateComponent 函数
      // 实际上执行vm._update(vm._render(), hydrating)
      // 首先执行vm_render方法,生成渲染VNode,并对vm上的数据访问,触发了数据对象的getter。
      value = this.getter.call(vm, vm);
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`);
      } else {
        throw e;
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        // 递归访问value,触发所有子项getter
        traverse(value);
      }
      // popTarget定义
      // 收集完把Dep.target恢复成上一个状态,
      // export function popTarget() {
      //   Dep.target = targetStack.pop();
      // }
      popTarget();
      this.cleanupDeps();
    }
    return value;
  }

  // 原型方法2
  // 每个对象值getter都持有一个dep,触发getter的时候会调用dep.depend,也就是执行Dep.target.addDep(this)
  addDep(dep: Dep) {
    const id = dep.id;
    // 做一些逻辑判断,保证同意数据不会多次添加
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id);
      this.newDeps.push(dep);
      if (!this.depIds.has(id)) {
        // 然后执行addSub,也会执行this.subs.push(sub)
        // 目的是为了后续数据变化时候通知subs做准备
        dep.addSub(this);
      }
    }
  }

  // 原型方法3
  // 依赖清空的方法
  // Vue是数据驱动的,每次数据变化都会重新render,vm._render再次触发getters,所以Watcher在构造函数初始化的时候初始化了两个数组newDeps和deps ,newDeps表示新添加的Dep实例数组,deps表示上一次添加的Dep实例数组。
  // 在cleanupDeps,先遍历上一次添加的Dep实例数组,也就是deps,移除对dep的订阅,然后把newDepIds 和 depIds 交换,把两个数组清空
  // cleanupDeps的目的是为了 a,b两个v-if的模版,先if到a模板,a模块触发某种情况,到了b模板,为了节约资源,cleanupDeps上次添加的Dep实例,去if b模板,if模板是一个vm._render的过程,cleanupDeps清除了订阅回调,就在if b模板的时候不再if a模板
  // cleanupDeps发现新的一轮订阅没有订阅,而且老的订阅数组里有该条订阅记录,就把该订阅者删掉,一旦没有订阅变化,去修改他不会触发重新渲染。
  cleanupDeps() {
    let i = this.deps.length;
    while (i--) {
      const dep = this.deps[i];
      if (!this.newDepIds.has(dep.id)) {
        dep.removeSub(this);
      }
    }
    let tmp = this.depIds;
    // 把newDepIds 和 depIds 交换
    this.depIds = this.newDepIds;
    this.newDepIds = tmp;
    this.newDepIds.clear();
    tmp = this.deps;
    this.deps = this.newDeps;
    this.newDeps = tmp;
    this.newDeps.length = 0;
  }
  // ...
}

共用代码

  1. mountComponent

    数据对象的访问会触发 getter 方法,数据什么时候访问呢?

// src/core/instance/lifecycle.js
// 当去实例化一个渲染watcher的时候先进入Watcher构造函数,执行this.get()方法
updateComponent = () => {
  vm._update(vm._render(), hydrating);
};
new Watcher(
  vm,
  updateComponent,
  noop,
  {
    before() {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate');
      }
    }
  },
  true /* isRenderWatcher */
);

暂无评论