根据vue源码解析watch的实现原理

引言

computed和watch都是基于wacther来实现的,渲染函数,computed和watch在源码中公用的一个watcher类,根据传入不同的参数来定义不同的wacther。

computed和watch的区别

1 computed的watcher默认不执行,需要用户获取的时候触发
2 computed有缓存效果,数据没有发生变化不会触发
3 computed是一对多
4 watch是多对一
5 watch支持异步

wacth用法

在讲解原理之前我们先看一看wacth的使用方法。wacth有三种使用方式:

1 字符串
2 数组
3 函数

1.字符串

    new Vue({
        watch: {
            name:'fn'
        },
        methods: {
            fn(newVal,oldVal){}
        }
    })

2.数组

 new Vue({
        watch: {
            name:[
                {
                    handler:'fn',
                    async:true
                }
            ]
        },
        methods: {
            fn(newVal,oldVal){}
        }
    })

3.函数

 new Vue({
        watch: {
            name(newVal,oldVal){ }
        }
    })

源码解析

1.初始化wacther

vm是new Vue时传进来的对象
function initState (vm) {
  ...
  const opts = vm.$options
//如果用户传进来wacth
  if (opts.watch) {
    initWatch(vm, opts.watch)
  }
}

2.处理传入上述三种不同的类型

function initWatch (vm, watch) {
//循环wacth对象
  for (const key in watch) {
  //handler接收每一项,这里是name
    const handler = watch[key]
    //如果name是数组的话
    if (Array.isArray(handler)) {
    //循环name这个数组
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
    //如果是函数或者字符串
      createWatcher(vm, key, handler)
    }
  }
}
function createWatcher (vm,expOrFn,handler,options) {
//如果是对象的话,因为数组的每一项是对象
  if (isPlainObject(handler)) {
  //options接收多余的值比如deep,async
    options = handler
    //这里传入的handler是对象{handler:'fn'},让handler指向这个函数名
    handler = handler.handler
  }
  //如果是字符串的话
  if (typeof handler === 'string') {
  //直接在实例上获取name这个属性,因为methods中的方法都能在实例上获取到,这里获取的是fn函数
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
                    name        fn      可能有,可能没有 async..
}

3.创建wacther

Vue.prototype.$watch = function (expOrFn,cb,options){
    const vm= this
    options = options || {}
   //Watcher使用的是渲染wacther,设置user是为了区分渲染wacther和用户传入的wacth
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
  }

wacther中的处理

class Watcher {
  constructor(vm, expOrFn, cb, options) {
    this.vm = vm
    this.user = !!options.user
    this.cb = cb
    //上面我们传的expOrFn是key值 如果是一个函数那就是渲染wacther
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
    //wacth方法 如果key是'a.b.c.d'那么这个方法parsePath是将d这个值从实例上一层一层的取出来,包装成一个函数  function(){return d} 如果不进行这层包装 下面方法会报错
      this.getter = parsePath(expOrFn)
    }
    //接收初始化的数据
    this.value = this.get()
  }

  get() {
  //依赖收集的操作将当前的wacther存放在Dep.target中
    pushTarget(this)
    let value = this.getter.call(this.vm)
    popTarget()
    return value
  }
//依赖收集的时候当数据发生改变时会触发这个方法
  update() {
         this.run()
  }

  run() {
  //改变之后的数据
        const value = this.get()
        //改变前的数据
        const oldValue = this.value
        this.value = value
        if (this.user) {//如果是wacth函数
        //直接将对应函数执行并且将最新的值和老的值传递过去
            this.cb.call(this.vm, value, oldValue)
  }
}

你可能感兴趣的:(watchvue.js)