Vue的双向绑定主要做了2件事

  1. 数据劫持
  2. 添加观察者


数据劫持(proxy)+依赖收集(观察者模式:watcher、Dep)

数据劫持

Object.defineProperty

  1. Object.defineProperty(obj, key, {
  2. enumerable: true,
  3. configurable: true,
  4. # 这里无非就是劫持了对象的getset方法。在所代理的属性的get方法中,
  5. # dep.Target存在的时候会调用 dep.depend(),
  6. get: function reactiveGetter () {
  7. const value = getter ? getter.call(obj) : val
  8. if (Dep.target) {
  9. dep.depend()
  10. if (childOb) {
  11. childOb.dep.depend()
  12. }
  13. if (Array.isArray(value)) {
  14. dependArray(value)
  15. }
  16. }
  17. return value
  18. },
  19. set: function reactiveSetter (newVal) {
  20. const value = getter ? getter.call(obj) : val
  21. if (newVal === value || (newVal !== newVal && value !== value)) {
  22. return
  23. }
  24. if (setter) {
  25. setter.call(obj, newVal)
  26. } else {
  27. val = newVal
  28. }
  29. childOb = !shallow && observe(newVal)
  30. dep.notify()
  31. }
  32. })

proxy

  1. // 最新版可以通过 Proxy 实现
  2. Proxy(data, {
  3. get(target, key) {
  4. return target[key];
  5. },
  6. set(target, key, value) {
  7. let val = Reflect.set(target, key, value);
  8. _that.$dep[key].forEach(item => item.update());
  9. return val;
  10. }
  11. })

观察者模式

btw:观察者模式 vs 发布订阅
eventemitter属于?

  1. // 订阅者:数据变更的消费者
  2. class Dep {
  3. constructor() {
  4. this.subs = []
  5. }
  6. addSub(sub) {
  7. this.subs.push(sub)
  8. }
  9. depend() {
  10. if (Dep.target) {
  11. Dep.target.addDep(this);
  12. }
  13. }
  14. notify() {
  15. this.subs.forEach(sub => sub.update())
  16. }
  17. }
  1. // 被观察者:数据变更了通知下游消费者
  2. class Watcher {
  3. constructor(vm, expOrFn) {
  4. this.vm = vm;
  5. this.getter = expOrFn;
  6. this.value;
  7. }
  8. get() {
  9. Dep.target = this;
  10. var vm = this.vm;
  11. var value = this.getter.call(vm, vm);
  12. return value;
  13. }
  14. evaluate() {
  15. this.value = this.get();
  16. }
  17. addDep(dep) {
  18. dep.addSub(this);
  19. }
  20. update() {
  21. console.log('更新, value:', this.value)
  22. }
  23. }
  1. // 观察者实例
  2. var dep = new Dep();
  3. // 被观察者实例
  4. var watcher = new Watcher({x: 1}, (val) => val);
  5. watcher.evaluate();
  6. // 观察者监听被观察对象
  7. dep.depend()
  8. dep.notify()
  1. 通过 watcher.evaluate() 将自身实例赋值给 Dep.target
  2. 调用 dep.depend() 将dep实例将 watcher 实例 push 到 dep.subs中
  3. 通过数据劫持,在调用被劫持的对象的 set 方法时,调用 dep.subs 中所有的 watcher.update()