# watch

export function watch<T = any>(
  source: WatchSource<T> | WatchSource<T>[],
  cb: WatchCallback<T>,
  options?: WatchOptions
): WatchStopHandle {
  // 校验传入的callback是不是函数
  if (__DEV__ && !isFunction(cb)) {
    warn(
      `\`watch(fn, options?)\` signature has been moved to a separate API. ` +
        `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
        `supports \`watch(source, cb, options?) signature.`
    )
  }
  // 调用doWatch
  return doWatch(source, cb, options)
}
function doWatch(
  source: WatchSource | WatchSource[] | WatchEffect,
  cb: WatchCallback | null,
  { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ,
  instance = currentInstance
): WatchStopHandle {
  if (__DEV__ && !cb) {
    if (immediate !== undefined) {
      warn(
        `watch() "immediate" option is only respected when using the ` +
          `watch(source, callback, options?) signature.`
      )
    }
    if (deep !== undefined) {
      warn(
        `watch() "deep" option is only respected when using the ` +
          `watch(source, callback, options?) signature.`
      )
    }
  }

  const warnInvalidSource = (s: unknown) => {
    warn(
      `Invalid watch source: `,
      s,
      `A watch source can only be a getter/effect function, a ref, ` +
        `a reactive object, or an array of these types.`
    )
  }

  // 根据传入的source类型创建getter
  // 用于重新获取监听的source的新值
  let getter: () => any
  const isRefSource = isRef(source)
  // 如果source是Ref,则返回其value值
  if (isRefSource) {
    getter = () => (source as Ref).value
  } else if (isReactive(source)) {
    // 如果source是响应式对象,
    // 返回本身,并将deep设置为true
    getter = () => source
    deep = true
  } else if (isArray(source)) {
    // 如果source是数组
    // 遍历数组,根据数组值的类型返回相应的值
    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)) {
    // 如果source是函数,并且存在callback
    // 则直接返回source执行的结果
    if (cb) {
      // getter with cb
      getter = () =>
        callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
    } else {
      // no cb -> simple effect
      // 如果source是函数,并且不存在callback
      // 那么这是通过watchEffect调用,则source可以接收一个参数onInvalidate用于注册cleanup函数
      getter = () => {
        if (instance && instance.isUnmounted) {
          return
        }
        if (cleanup) {
          cleanup()
        }
        return callWithErrorHandling(
          source,
          instance,
          ErrorCodes.WATCH_CALLBACK,
          [onInvalidate]
        )
      }
    }
  } else {
    // 如果source不满足以上任何一种
    // 将getter赋值为() => {}
    // 提示source类型错误
    getter = NOOP
    __DEV__ && warnInvalidSource(source)
  }

  // 如果deep为true,则调用traverse将getter的返回值访问一遍
  // 目的是将所有返回值的所有key添加到依赖中
  if (cb && deep) {
    const baseGetter = getter
    getter = () => traverse(baseGetter())
  }
  // 注册cleanup函数,它将在watch effect停止或者每次执行回调前调用
  let cleanup: () => void
  const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => {
    cleanup = runner.options.onStop = () => {
      callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
    }
  }

  // in SSR there is no need to setup an actual effect, and it should be noop
  // unless it's eager
  // SSR相关逻辑,跳过
  if (__NODE_JS__ && isInSSRComponentSetup) {
    if (!cb) {
      getter()
    } else if (immediate) {
      callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
        getter(),
        undefined,
        onInvalidate
      ])
    }
    return NOOP
  }

  // 获取value的初始旧值,如果source是数组则为[],否则就是undefined
  let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE
  // 创建传入effect option的Scheduler
  const job: SchedulerJob = () => {
    // 如果effect已经被停止,直接返回
    if (!runner.active) {
      return
    }
    // 如果存在callback
    if (cb) {
      // watch(source, cb)
      // 调用effect重新获得getter的值
      const newValue = runner()
      // 如果deep == true 或者 是ref source 或者新旧value不同,则触发callback执行
      if (deep || isRefSource || hasChanged(newValue, oldValue)) {
        // cleanup before running cb again
        if (cleanup) {
          cleanup()
        }
        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 : oldValue,
          onInvalidate
        ])
        oldValue = newValue
      }
    } else {
      // watchEffect
      // 如果不存在callback,则简单的重新执行getter
      runner()
    }
  }

  // important: mark the job as a watcher callback so that scheduler knows it
  // it is allowed to self-trigger (#1727)
  // 如果存在callback,那么watch可以递归触发自身
  job.allowRecurse = !!cb

  // 根据flush的值来决定触发的回调是在组件更新前、后或者是同步执行
  let scheduler: (job: () => any) => void
  if (flush === 'sync') {
    // 同步执行,直接将job赋值给scheduler,触发effect时直接执行
    scheduler = job
  } else if (flush === 'pre') {
    // ensure it's queued before component updates (which have positive ids)
    // 组件更新前执行
    // 将id设置为-1,确保函数在队列中最早执行
    job.id = -1
    scheduler = () => {
      if (!instance || instance.isMounted) {
        queuePreFlushCb(job)
      } else {
        // with 'pre' option, the first call must happen before
        // the component is mounted so it is called synchronously.
        job()
      }
    }
  } else {
    // 组件更新后执行
    scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
  }

  // 创建watch effect
  const runner = effect(getter, {
    lazy: true,
    onTrack,
    onTrigger,
    scheduler
  })
  // 将当前effect保存到实例中
  // 方便组件销毁时停止effect
  recordInstanceBoundEffect(runner)

  // initial run
  // 如果存在回调函数,并且immediate = true
  // 则直接执行回调函数,根据flush来决定执行的时机
  // 否则先调用runner计算oldValue的值
  // 如果不存在回调函数,则是通过watchEffect这个api调用
  // 那么直接执行getter
  if (cb) {
    if (immediate) {
      job()
    } else {
      oldValue = runner()
    }
  } else {
    runner()
  }
  // 返回一个停止effect的函数,并将effect从当前实例移除
  return () => {
    stop(runner)
    if (instance) {
      remove(instance.effects!, runner)
    }
  }
}