vue 双向绑定源码

275 阅读1分钟
class Watcher {
  constructor(target, key, cb) {
    Dep.targetWatcher = this;
    this.cb = cb;
    this.value = this.get(target, key);
    Dep.targetWatcher = null;
  }
  get(target, key) {
    return target[key];
  }
  run() {
    this.cb();
  }
}

class Dep {
  constructor() {
    this.subs = [];
  }
  addDep(watcher) {
    this.subs.push(watcher);
  }
  notify() {
    this.subs.forEach((watcher) => {
      watcher.run();
    });
  }
}
Dep.targetWatcher = null;

function observer(data) {
  Object.keys(data).forEach((key) => {
    defineReactive(data, key);
  });
  function defineReactive(target, key) {
    let dep = new Dep();
    let val = target[key];
    Object.defineProperty(target, key, {
      get() {
        console.log("getter");
        if (Dep.targetWatcher) {
          dep.addDep(Dep.targetWatcher);
        }
        return val;
      },
      set(newValue) {
        console.log("setter");
        val = newValue;
        dep.notify();
      },
    });
  }
}

let state = {
  a: 1,
  b: 2,
};
observer(state);

new Watcher(state, "a", () => {
  console.log("a has change", state.a);
});
new Watcher(state, "b", () => {
  console.log("b has change", state.b);
});

setTimeout(() => {
  state.a = 2;
}, 1000);
setTimeout(() => {
  state.a = 2;
}, 500);