vue2 Object 数据双向绑定源码阅读

137 阅读1分钟

核心属性方法:

  1. Object.defineProperty: 监听绑定对象;
  2. Dep: 收集依赖,通知更新;
  3. Watch: 订阅更新,通知视图更新;

组件思路:数据获取时收集依赖,数据更新变化时通知更新;

defineProperty:

function defineReactive (obj,key,val) {
  if (arguments.length === 2) {
    val = obj[key]
  }
  if(typeof val === 'object'){
    new Observer(val)
  }
  const dep = new Dep()  //实例化一个依赖管理器,生成一个依赖管理数组dep
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get(){
      dep.depend()    // 在getter中收集依赖
      return val;
    },
    set(newVal){
      if(val === newVal){
          return
      }
      val = newVal;
      dep.notify()   // 在setter中通知依赖更新
    }
  })
}

Dep:

export default class Dep {
  constructor () {
    this.subs = []
  }

  addSub (sub) {
    this.subs.push(sub)
  }
  // 删除一个依赖
  removeSub (sub) {
    remove(this.subs, sub)
  }
  // 添加一个依赖
  depend () {
    if (window.target) {
      this.addSub(window.target)
    }
  }
  // 通知所有依赖更新
  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

Watch:

export default class Watcher {
  constructor (vm,expOrFn,cb) {
    this.vm = vm;
    this.cb = cb;
    this.getter = parsePath(expOrFn)
    this.value = this.get()
  }
  get () {
    window.target = this;
    const vm = this.vm
    let value = this.getter.call(vm, vm)
    window.target = undefined;
    return value
  }
  update () {
    const oldValue = this.value
    this.value = this.get()
    this.cb.call(this.vm, this.value, oldValue)
  }
}

大致流程:

  1. Data通过observer转换成了getter/setter的形式来追踪变化。
  2. 当外界通过Watcher读取数据时,会触发getter从而将Watcher添加到依赖中。
  3. 当数据发生了变化时,会触发setter,从而向Dep中的依赖(即Watcher)发送通知。
  4. Watcher接收到通知后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某个回调函数等。

不足之处:仅只能观测到object数据的取值及设置值;

  1. 数据里添加一对新的key/value,无法追踪到;

    ---Vue.set( target, propertyName/index, value )解决;

  2. 数据里删除一对新的key/value,无法追踪到;

    ---vue.delete(target, propertyName/index)解决;

双向绑定.jpg