• 【Vue3 源码解析】watch 侦听器


    export function watch<T = any, Immediate extends Readonly<boolean> = false>(
      source: T | WatchSource<T>,
      cb: any,
      options?: WatchOptions<Immediate>
    ): WatchStopHandle {
      // 在开发环境下,如果回调函数不是函数,则发出警告
      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 as any, cb, options)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这段代码是 watch 函数的一个重载形式,用于在开发环境下检查回调函数的类型,并调用 doWatch 函数来实际执行监视操作。

    解释:

    • 这段代码导出了一个名为 watch 的函数,它接受三个参数:sourcecboptionssource 表示要监视的数据源,cb 表示回调函数options 表示监视的配置选项。

    • 在开发环境下,通过 __DEV__ 条件判断检查回调函数 cb 是否是一个函数。如果不是函数,则发出警告,提示用户 watch(fn, options?) 的签名已经被移动到单独的 API,建议使用 watchEffect(fn, options?) 代替。

    • 最后,调用 doWatch 函数来执行监视操作,并返回一个 WatchStopHandle 函数,用于停止监视。

    function doWatch(
      source: WatchSource | WatchSource[] | WatchEffect | object,
      cb: WatchCallback | null,
      { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
    ): WatchStopHandle {
      // 在开发环境下,如果没有提供回调函数 cb,但提供了 immediate 或 deep 选项,
      // 则发出警告,因为这些选项只对特定的签名有效。
      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.`
        )
      }
    
      // 获取当前组件实例
      const instance =
        getCurrentScope() === currentInstance?.scope ? currentInstance : null
    
      let getter: () => any
      let forceTrigger = false
      let isMultiSource = false
    
      // 根据不同类型的数据源,设置相应的 getter 和 forceTrigger 值
      // 如果是 ref 对象
      if (isRef(source)) {
      	// 创建 getter 并读取 ref 对象的 value
        getter = () => source.value
        forceTrigger = isShallow(source)
      } else if (isReactive(source)) {
      	// 如果是 reactive 对象,直接返回 getter 函数,设置 deep 为 true 
        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)) {
              // traverse 就是一个递归,相当于深度监听
              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,即提供了回调函数 cb
          getter = () =>
            callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
        } else {
          // no cb -> simple effect,即未提供回调函数 cb
          getter = () => {
            if (instance && instance.isUnmounted) {
              return
            }
            if (cleanup) {
              cleanup()
            }
            return callWithAsyncErrorHandling(
              source,
              instance,
              ErrorCodes.WATCH_CALLBACK,
              [onCleanup]
            )
          }
        }
      } else {
        getter = NOOP // 无效的监视源,设置 getter 为 NOOP 函数
        __DEV__ && warnInvalidSource(source)
      }
    
      // 如果开启了 2.x 兼容性选项且提供了回调函数 cb 且 deep 为 false,则添加兼容性支持
      if (__COMPAT__ && cb && !deep) {
        const baseGetter = getter
        getter = () => {
          const val = baseGetter()
          if (
            isArray(val) &&
            checkCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance)
          ) {
            traverse(val)
          }
          return val
        }
      }
    
      // 如果设置了 deep 选项,对 getter 的返回值进行递归深度监听 traverse
      if (cb && deep) {
        const baseGetter = getter
        getter = () => traverse(baseGetter())
      }
    
      let cleanup: () => void
      let onCleanup: OnCleanup = (fn: () => void) => {
        cleanup = effect.onStop = () => {
          callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
        }
      }
    
      // 在服务器端渲染 (SSR) 模式下,不需要设置实际的 effect,它应该是无操作的,除非设置了 eager 或 sync flush 选项
      let ssrCleanup: (() => void)[] | undefined
      if (__SSR__ && isInSSRComponentSetup) {
        // 在 SSR 中,不会调用 invalidate 回调函数(+ runner 未设置)
        onCleanup = NOOP
        if (!cb) {
          getter() // 在 SSR 情况下立即获取数据
        } else if (immediate) {
          // 在 SSR 情况下,如果设置了 immediate 选项,则立即调用回调函数 cb
          callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
            getter(),
            isMultiSource ? [] : undefined,
            onCleanup
          ])
        }
        if (flush === 'sync') {
          // 在 SSR 情况下,如果设置了 sync flush,则获取 SSR 上下文,并将 cleanup 函数添加到 SSR 清理句柄中
          const ctx = useSSRContext()!
          ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = [])
        } else {
          return NOOP // 其他情况下,返回无操作函数
        }
      }
    
      // 初始化 oldValue,根据数据源是否为多源(数组)进行初始化
      let oldValue: any = isMultiSource
        ? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
        : INITIAL_WATCHER_VALUE
    
      // 定义要运行的任务(job)函数,根据 cb 是否存在执行不同逻辑
      const job: SchedulerJob = () => {
        if (!effect.active) {
          return
        }
        if (cb) {
          // watch(source, cb) 情况
          const newValue = effect.run()
          if (
            deep ||
            forceTrigger ||
            (isMultiSource
              ? (newValue as any[]).some((v, i) => hasChanged(v, oldValue[i]))
              : hasChanged(newValue, oldValue)) ||
            (__COMPAT__ &&
              isArray(newValue) &&
              isCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance))
          ) {
            // 在运行回调函数 cb 之前清理
            if (cleanup) {
              cleanup()
            }
            callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
              newValue,
              // 当首次变化时(INITIAL_WATCHER_VALUE 为 {})将旧值传递为 undefined
              oldValue === INITIAL_WATCHER_VALUE ? undefined : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue, onCleanup
            ])
            // 彩蛋!!基础笔记中:`如果是引用类型(比如直接监听最外层 message)(此时已开启深度监听),则返回的新值和旧值是一样的(深层也是一样的,都是新值的数值)` 
            // 原因就在此:直接将引用(地址)赋值,深层也必定一样
            oldValue = newValue
          }
        } else {
          // watchEffect 情况
          effect.run()
        }
      }
    
      // 重要:将任务标记为 watcher 回调,以便调度器知道可以自行触发它 (#1727)
      job.allowRecurse = !!cb
      // 执行不同调度
      let scheduler: EffectScheduler
    
      // 根据 flush 选项设置调度器
      if (flush === 'sync') {
        scheduler = job as any // 直接调用任务函数
      } else if (flush === 'post') {
      	// queuePostRenderEffect 组件更新之后执行
        scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
      } else {
        // 默认情况下使用 'pre'
        job.pre = true
        if (instance) job.id = instance.uid
        scheduler = () => queueJob(job)
      }
    
      // 创建响应式 effect
      const effect = new ReactiveEffect(getter, scheduler)
    
      if (__DEV__) {
        effect.onTrack = onTrack
        effect.onTrigger = onTrigger
      }
    
      // 初始化运行任务
      if (cb) {
        if (immediate) {
          job() // 立即运行任务
        } else {
          oldValue = effect.run() // 初始化 oldValue
        }
      } else if (flush === 'post') {
        // 如果是 watchEffect,且 flush 为 'post',则将任务添加到 post-render 队列中执行
        queuePostRenderEffect(
          effect.run.bind(effect),
          instance && instance.suspense
        )
      } else {
        effect.run() // 初始化运行任务
      }
    
      // 定义停止监视的函数
      const unwatch = () => {
        effect.stop() // 停止 effect
        if (instance && instance.scope) {
          remove(instance.scope.effects!, effect) // 从组件实例中移除 effect
        }
      }
    
      // 在 SSR 情况下,将 cleanup 函数添加到清理句柄中
      if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
    
      // 返回停止监视的函数
      return unwatch
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245

    上述代码实现了watch的核心逻辑。

    它根据不同的数据源类型,设置不同的getter函数,然后在数据源发生变化时执行回调函数或效果函数。它还支持immediatedeepflush等选项,以及在服务器端渲染(SSR)环境下的特殊处理。

    这段代码的核心是创建一个响应式effect,并在需要时执行回调函数或效果函数,以实现数据的监视和响应。

  • 相关阅读:
    程序员们有什么好的编程习惯?
    TS类中初始化属性语法糖
    C#中从一个路径复制SQLite数据库并将其粘贴到另一路径
    【Python】科研代码学习:九 parser,argparse,HfArgumentParser
    ERP对接淘宝/天猫/京东/拼多多商品详情数据API接口
    一文简述AI自动化漏洞修复实践
    FrameWork之旅 -- Activity过去的门面ActionBar
    北京地标-自动驾驶高精度地图特征定位数据技术规范
    C# 图解教程 第5版 —— 第6章 方法
    2022牛客暑期多校训练营5(BCDFGHK)
  • 原文地址:https://blog.csdn.net/XiugongHao/article/details/133241290