Appearance
watch解读 
在组合式api如何使用watch呢? 
watch方法是在packages\runtime-core\src\apiWatch.ts里面,主要是doWatch函数执行dowatch对监听值的写法进行好几种分别处理
js
let getter: () => any
if (isRef(source)) {
    getter = () => source.value
    forceTrigger = isShallow(source)
  } else if (isReactive(source)) {
    getter = () => source
    deep = true
  } else if (isArray(source)) {
    isMultiSource = true
    forceTrigger = source.some(s => isReactive(s) || isShallow(s))
    getter = () =>
      source.map(s => {
        if (isRef(s)) {
          return s.value
        } else if (isReactive(s)) {
          return traverse(s)
        } else if (isFunction(s)) {
          return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
        } else {
          __DEV__ && warnInvalidSource(s)
        }
      })
  } else if (isFunction(source)) {
    if (cb) {
      // getter with cb
      getter = () =>
        callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
    } else {
      // no cb -> simple effect
      getter = () => {
        if (instance && instance.isUnmounted) {
          return
        }
        if (cleanup) {
          cleanup()
        }
        return callWithAsyncErrorHandling(
          source,
          instance,
          ErrorCodes.WATCH_CALLBACK,
          [onCleanup]
        )
      }
    }
  } else {
    getter = NOOP
    __DEV__ && warnInvalidSource(source)
  }
2-1. 对传入值souce值的处理
如果传入是一个ref类型的值,赋值
getter = () => source.value,这种写法有一个特点就是不会立马触发 ref对象的get value() {}方法如果传入一个reactive类型的之后,赋值
getter = () => source, 特点是,deep变成true, 自动设置深度监听如果是一个数组类型(看起来挺复杂的,有空再解读吧^_^)
如果是是一个函数, 并且有回调函数,赋值
getter = () => callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)上述几种情况都不存在,赋值
getter = NOOP, 赋值一个空函数
3:如果有回调函数和deep为true,就会对getter再次进行封装一层
js
if (cb && deep) {
    const baseGetter = getter
    getter = () => traverse(baseGetter())
}
// 递归遍历对象,触发get操作符,收集watch依赖
export function traverse(value: unknown, seen?: Set<unknown>) {
  if (!isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
    return value
  }
  seen = seen || new Set()
  if (seen.has(value)) {
    return value
  }
  seen.add(value)
  if (isRef(value)) {
    traverse(value.value, seen)
  } else if (isArray(value)) {
    for (let i = 0; i < value.length; i++) {
      traverse(value[i], seen)
    }
  } else if (isSet(value) || isMap(value)) {
    value.forEach((v: any) => {
      traverse(v, seen)
    })
  } else if (isPlainObject(value)) {
    for (const key in value) {
      traverse((value as any)[key], seen)
    }
  }
  return value
}
traverse(packages\runtime-core\src\apiWatch.ts)是递归获取对象值,让每一个属性都可以收集watch依赖,也就是可以深度监听值的核心步骤
4:定义job函数,该函数会传入scheduler里面,作为异步队列执行的函数
5:new一个依赖函数const effect = new ReactiveEffect(getter, scheduler)getter就是触发对象get的函数,scheduler就是异步执行的任务队列,多次触发get,确保只是执行一次
6:执行effect.run()
js
if (cb) {
  if (immediate) {
    job()
  } else {
    oldValue = effect.run()
  }
} else if (flush === 'post') {
  queuePostRenderEffect(
    effect.run.bind(effect),
    instance && instance.suspense
  )
} else {
  effect.run()
}
- 如果定义了immediate为true,会执行回调函数
 
js
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
  newValue,
  // pass undefined as the old value when it's changed for the first time
  oldValue === INITIAL_WATCHER_VALUE
    ? undefined
    : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE
    ? []
    : oldValue,
  onCleanup
])
6-1: 执行effect.run(),这个收集依赖的核心函数, 实际上执行ReactiveEffect构造函数的effect方法
js
run() {
    // debugger
    if (!this.active) {
      return this.fn()
    }
    let parent: ReactiveEffect | undefined = activeEffect
    let lastShouldTrack = shouldTrack
    while (parent) {
      if (parent === this) {
        return
      }
      parent = parent.parent
    }
    try {
      this.parent = activeEffect
      activeEffect = this
      shouldTrack = true
      trackOpBit = 1 << ++effectTrackDepth
      if (effectTrackDepth <= maxMarkerBits) {
        initDepMarkers(this)
      } else {
        cleanupEffect(this)
      }
      return this.fn()
    } finally {
      if (effectTrackDepth <= maxMarkerBits) {
        finalizeDepMarkers(this)
      }
      trackOpBit = 1 << --effectTrackDepth
      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined
      if (this.deferStop) {
        this.stop()
      }
    }
}
activeEffect赋值给this.parent, 保存当前的对象赋值给activeEffect
js
 try {
      this.parent = activeEffect
      activeEffect = this
      shouldTrack = true
      return this.fn()
    } finally {
    }
this.fn()就是开始定义的getter函数,用于触发对象的get操作符,搜集当前的作为依赖
7:最后返回unwatch方法,可以用于停止执行当前watch的触发
js
const unwatch = () => {
    effect.stop()
    if (instance && instance.scope) {
      remove(instance.scope.effects!, effect)
    }
  }
  if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
  return unwatch
总结 
执行watch监听的时候,会执行一系列的初始化操作,如:
1:判断监听参数的类型,二次封装callback函数
2:new一个依赖函数
const effect = new ReactiveEffect(getter, scheduler),gettter包含callback3: 执行
effect.run(), 把当前依赖赋值到全局依赖变量activeEffect里面,然后递归执行traverse函数,触发监听参数的getter操作符,触发依赖收集4:还原原本的依赖
activeEffect = this.parent
最终callback函数,就被收集到 类似:
js
targetMap = {
    {msg: 'hello vue'}: {
      msg: [ReactiveEffect2, ReactiveEffect],
    }
}
watch的不同写法 
js
var { ref, watch, reactive } = Vue
var test = ref({});
var react = reactive({})
// 写法一
watch(test, () => {})
// 写法二
watch(test.value, () => {})
// 写法三
watch(() => test.value, () => {})
// 写法四
watch(react, () => {})
相关代码 
html
<div id="app">
    <input v-model="test.name" />
</div>
<script>  
    var { createApp, ref, watch  } = Vue;
    var app = createApp({
        setup() {
            var test = ref({  });
            onMounted(() => {
              // debugger
              test.value = { name: 1 }
              
            })
            
            watch(test, () => {
              console.log(test)
            })
            return {
                test,
            }
        }
    })
    app.mount('#app')
  </script>