vue源码——watcher

233 阅读2分钟

watch的调用方式

常见的场景有下面这几个:

1. 数据变 → 使用数据的视图变(负责敦促视图更新的render-watcher)

vue实例或者component组件通过调用$mount方法生成render函数(不管实例当中使用的是el/template,最终都会编译成render),在初始化vue实例或者组件的时候,会为render函数绑定watcher监听事件。
文件路径:src/core/instance/lifecycle.js文件下,对渲染函数的绑定。下面将会详细讲wather的绑定

image.png

2. 数据变 → 使用数据的计算属性变 → 使用计算属性的视图变(执行敦促计算属性更新的computed-watcher)


文件路径:src/core/instance/state.js文件下

image.png

image.png

3. 数据变 → 开发者主动注册的watch回调函数执行(用户注册的普通watcher(watch-api或watch属性))


文件路径:src/core/instance/state.js文件下 watch的使用

watch: {
    name: [function() {
        console.log('kkkkkkkkk');
    }, function() {
        console.log('gggggggggg');
    }], // 数组
    name: function() {
        console.log('ssssss');
    }, // 普通函数
    name: {
        handler: function (val, oldVal) {

        },
        deep: true
    }, // 对象
    name: {
        handler: 'testName' // testName对应methods里面的方法名
    }
},

image.png

image.png

watcher 文件的说明

watcher是放在src/core/observe/watcher.js里面

1. watcher初始化,即constructor方法

image.png

2. get函数

get函数里面调用了getter,以下是对get的说明

  1. 一个data中的属性对应对应一个dep, 一个dep中可能包含多个watcher(模板中可能使用了计算属性、组件等,都有可能引起多个watcher)
  2. 模板中一个非事件表达式对应一个watcher, 一个watcher中可能包含多个dep(表达式中包含了几个data属性, 或者是render函数中,用了多个属性)
getter 函数,有3种形式。
    第一种,是render函数
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
    第二种: 计算属性的函数
    watchers[key] = new Watcher(
      vm,
      getter || noop,
      noop,
      computedWatcherOptions
    )
    第二种,字符串,获取值的的字符串,譬如下面调用
    当这种方式使用watcher的时候
    watch: {
      name: function() {
          console.log('kkkkkkkkk');
      }
    }
    那么生成watcher的时候,通过parsePath生产一个获取表达式值的函数
    parsePath(expOrFn)
    
  getter函数的执行原理(getter函数里面用到了那些数据,就给这个数据的dep加上watcher。同时也给watcher加上dep)
    1、 在生成watcher的时候,对props、data都进行了双向数据绑定,即有get和set方法。一个data属性对应一个订阅器(dep)
    2、 在get的方法的里面,有以下操作,把watcher添加到dep里面,也把dep添加到watcher里面
        if (Dep.target) { 
          dep.depend()
          if (childOb) {
            childOb.dep.depend() // 如果有child, 也要给child属性加watcher
            if (Array.isArray(value)) {
              dependArray(value)
            }
          }
        }
    3. 不管是哪一种方式,调用this.getter的时候,都会获取getter里面的获取里面的属性的值譬如this.name来获取name的值。这时候就会调用数据绑定中的get方法
       就会执行2上面的方法,订阅器和watcher会相互添加。

image.png

image.png