在vue2中 Data、Observer、Dep和Watcher 之间是如何工作的

149 阅读1分钟

一、创建 Observer.js


import Dep from "./dep.js";

export default class Observer {
  constructor(data) {
    this.walk(data);
  }

  walk(data) {
    Object.keys(data).forEach((key) => {
      defineReactive(data, key, data[key]);
    });
  }
}

function defineReactive(obj, key, val) {
  const dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      if (window.target) {
        dep.depend();
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      dep.notify();
    },
  });
}

二、创建Dep.js

export default class Dep {
  constructor() {
    this.subs = []; //收集wacher
  }

  depend() {
    if (window.target) {
      this.subs.push(window.target);
    }
  }

  notify() {
    this.subs.forEach((sub) => sub.update());
  }
}

三、创建Watcher

export default class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm;
    this.getter = parsePath(expOrFn);
    this.cb = cb;
    this.value = this.get();
  }

  get() {
    window.target = this;
    let value = this.getter.call(this.vm, this.vm);
    window.target = undefined;
    return value;
  }

  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.cb.call(this.vm, this.value, oldValue);
  }
}

function parsePath(path) {
  const segments = path.split(".");
  return function (obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return;
      obj = obj[segments[i]];
    }
    return obj;
  };
}

四、在index.js 中调用

import Observer from "./Observer";
import Watcher from "./watcher.js";
const vm = {
  data: {
    messsage: {
      name: "hello",
    },
  },
};

new Observer(vm.data);
new Watcher(vm, "data.messsage.name", (value, oldValue) => {
  console.log(value, oldValue);
});
vm.data.messsage.name = "world";

首先看一下下面的图

image.png 创建一个vm 使用Observer遍历vm的每一项 使用Object.defineProperty 劫持数据 并且new 了一个dep此时并没有依赖收集 下面我们去new 一个watcher 类似一个中介 watcher 内部触发getter方法的时候 会触发defineProperty中的get 并且我们把当前的watcher 挂载到window.target上所以dep内部会把一个一个的watcher存在sub中 watcher 基本上分为三大类 渲染watcher 计算属性watcher 监听watcher