watch和this.$watch的原理

382 阅读1分钟
// vm.$watch 和 watch
function initWtch(vm, watch) {
  console.log(watch);
  for (const key in watch) {
    let handle = watch[key]
    if (Array.isArray(handle)) {
      for (let index = 0; index < handle.length; index++) {
        const element = handle[index];
        createWatcher(vm, key, element)
      }
    } else {
      createWatcher(vm, key, handle)
    }
  }
}

export function stateMixin(Vue) {
  Vue.prototype.$watch = function (key, handle,options={}) {
    console.log(key, handle);
    options.user = true // 用来区分这是用户watcher
    // 创建渲染watcher
    new Watcher(this,key,handle,options)
  }
}

  constructor(vm, exprFunc, callback, options) {
    this.vm = vm;
        //有可能为underfined  取bool值
    this.user = !!options.user
    // 这里判断一下如果exprFunc为string就是用户watcher
    if (typeof exprFunc == 'string') {
      this.exprFunc = function () {
        // 'vm.a.a.a.b'
        let arr = exprFunc.split(',')
        let obj = arr.reduce((vm, item, index) => {
          return vm[item]
        }, vm)
        return obj
      }
      // 这里取值完成后自动会将属性的dep在收集一个watcher,所以一个属性dep会对应多个watcher
    }
    else {
      this.exprFunc = exprFunc;
    }

    this.callback = callback;

    this.options = options;
    // 自增标识符
    this.id = id++;
    this.watchers = [];
    this.idSet = new Set();

    // 自动执行render  这里会得到一个value值
    this.value =  this.getters();
  }

  //当渲染时自动会执行渲染
  getters() {
    //取值之前先把自己挂载到dep上的静态属性,类似于全局后面变量取值时会将dep和watcher关联
    pushTarget(this);
    const value =  this.exprFunc();
    popTarget();
    return value
  }

  upData() {
    // if(this)

    let  newValue =  this.exprFunc();
    let oldValue = this.value
    this.value = newValue
    // 属性改变时watch 只有user为true时获取值
    if(this.user){
      this.callback.call(this.vm,newValue,oldValue)
    }
  }


function createWatcher(vm, key, handle) {
  return vm.$watch(key, handle)
}

watch的实现原理总结

  1. 当初始化watch的时候,循环watch,并且 new watcher() 然后watcher内部会将key编成函数取值赋值给变量,调用编写完的函数取值,然后将当前监听的watcher放到Dep.target上函数取值的结果付给watcher的value,当取值的时候会走到属性的getter函数,将每个属性上的dep属性进行watcher收集,并且watcher也收集当前dep,进行相互收集。

  2. 遍历完ast数后,执行render会取值,这里也会收集一遍渲染watcher

  3. 当属性改变后,回调用dep上的depend方法将对应的watchers进行相应的更新,这时,会重新取到最新的值,然后调用watcher的get方法,进行watch的回调并且重新执行render方法重新取值渲染视图