Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Vue3 源码解析(十):watch 的实现原理

Vue3 源码解析(十):watch 的实现原理

作者头像
Originalee
发布于 2021-06-21 12:16:42
发布于 2021-06-21 12:16:42
1.6K00
代码可运行
举报
文章被收录于专栏:编程之旅编程之旅
运行总次数:0
代码可运行

本篇文章笔者会讲解 Vue3 中侦听器相关的 api:watchEffect 和 watch 。在 Vue3 之前 watch 是 option 写法中一个很常用的选项,使用它可以非常方便的监听一个数据源的变化,而在 Vue3 中随着 Composition API 的写法推行也将 watch 独立成了一个 响应式 api,今天我们就一起来学习 watch 相关的侦听器是如何实现的。

? 储备知识要求:

在阅读本文前,建议你已经学习过本系列的第 7 篇文章的 effect 副作用函数的相关知识,否则在讲解副作用的相关部分可能会出现不理解的情况。

watchEffect

由于 watch api 中的许多行为都与 watchEffect api 一致,所以笔者将 watchEffect 放在首位讲解,为了根据响应式状态自动应用和重新应用副作用,我们可以使用 watchEffect 方法。它立即执行传入的一个函数,同时响应式追踪其依赖,并在以来变更时重新运行该函数。

watchEffect 函数的实现非常简洁:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export function watchEffect(
  effect: WatchEffect,
  options?: WatchOptionsBase
): WatchStopHandle {
  return doWatch(effect, null, options)
}

首先来看参数类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export type WatchEffect = (onInvalidate: InvalidateCbRegistrator) => void

export interface WatchOptionsBase {
  flush?: 'pre' | 'post' | 'sync'
  onTrack?: ReactiveEffectOptions['onTrack']
  onTrigger?: ReactiveEffectOptions['onTrigger']
}

export type WatchStopHandle = () => void

第一个参数 effect,接收函数类型的变量,并且在这个函数中会传入 onInvalidate 参数,用以清除副作用。

第二个参数 options 是一个对象,在这个对象中有三个属性,你可以修改 flush 来改变副作用的刷新时机,默认为 pre,当修改为 post 时,就可以在组件更新后触发这个副作用侦听器,改同 sync 会强制同步触发。而 onTrack 和 onTrigger 选项可以用于调试侦听器的行为,并且两个参数只能在开发模式下工作。

参数传入后,函数会执行并返回 doWatch 函数的返回值。

由于 watch api 也会调用 doWatch 函数,所以 doWatch 函数的具体逻辑我们会放在后边讲。先看 watch api 的函数实现。

watch

这个独立出来的 watch api 与组件中的 watch option 是完全等同的,watch 需要侦听特定的数据源,并在回调函数中执行副作用。默认情况下这个侦听是惰性的,即只有当被侦听的源发生变化时才执行回调。

与 watchEffect 相比,watch 有以下不同:

  • 懒性执行副作用
  • 更具体地说明说明状态应该处罚侦听器重新运行
  • 能够访问侦听状态变化前后的值

watch 函数的函数签名有许多种重载情况,且代码行数较多,所以笔者不准备分析每个重载情况,一起来看一下 watch api 的实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.`
    )
  }
  return doWatch(source as any, cb, options)
}

watch 接收 3 个参数,source 侦听的数据源,cb 回调函数,options 侦听选项。

source 参数

source 的类型如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
type MultiWatchSources = (WatchSource<unknown> | object)[]

从两个类型定义看出,数据源支持传入单个的 Ref、Computed 响应式对象,或者传入一个返回相同泛型类型的函数,以及 source 支持传入数组,以便能同时监听多个数据源。

cb 参数

在这个最通用的声明中,cb 的类型是 any,但是其实 cb 这个回调函数也有他自己的类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export type WatchCallback<V = any, OV = any> = (
  value: V,
  oldValue: OV,
  onInvalidate: InvalidateCbRegistrator
) => any

在回调函数中,会提供最新的 value、旧 value,以及 onInvalidate 函数用以清除副作用。

options

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
  immediate?: Immediate
  deep?: boolean
}

可以看到 options 的类型 WatchOptions 继承了 WatchOptionsBase,这也就是 watch 除了 immediate 和 deep 这两个特有的参数外,还可以传递 WatchOptionsBase 中的所有参数以控制副作用执行的行为。

分析完参数后,可以看到函数体内的逻辑与 watchEffect 几乎一致,但是多了在开发环境下检测回调函数是否是函数类型,如果回调函数不是函数,就会报警。

执行 doWatch 时的传参与 watchEffect 相比,多了第二个参数回调函数。

下面就让我们揭开这个终极 boss doWatch 的庐山真面目吧。

doWatch

不管是 watchEffect、watch 还是组件内的 watch 选项,在执行时最终调用的都是 doWatch 中的逻辑,这个强大的 doWatch 函数为了兼容各个 api 的逻辑源码也是挺长的大约有 200 行,所以老规矩,笔者会将长源码拆分开来讲。若想阅读完整源码请戳这里

先从 doWatch 的函数签名看起:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function doWatch(
  source: WatchSource | WatchSource[] | WatchEffect | object,
  cb: WatchCallback | null,
  { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ,
  instance = currentInstance
): WatchStopHandle

这个函数签名与 watch 基本一致,多了一个 instance 的参数,默认值为 currentInstance,currentInstance 是当前调用组件暴露出来的一个变量,方便该侦听器找到自己对应的组件。

而 source 在这里的类型就比较清晰,支持单个的 source 或者数组,也只是一个普通对象。

接着会创建三个变量,getter 最终会当做副作用的函数参数传入,forceTrigger 标识是否需要强制更新,isMultiSource 标记传入的是单个数据源还是以数组形式传入的多个数据源。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let getter: () => any
let forceTrigger = false
let isMultiSource = false

然后会开始判断 source 的类型,根据不同的类型重置这三个参数的值。

  • ref 类型
    • 访问 getter 函数会获取到 source.value 值,直接解包。
    • forceTrigger 标记会根据是否是 shallowRef 来设置。
  • reactive 类型
    • 访问 getter 函数直接返回 source,因为 reactive 的值不需要解包获取。
    • 由于 reactive 中往往有多个属性,所以会将 deep 设置为 true,这里可以看出从外部给 reactive 设置 deep 是无效的。
  • 数组 array 类型
    • 将 isMultiSource 设置为 true。
    • forceTrigger 会根据数组中是否存在 reactive 响应式对象来判断。
    • getter 是一个数组形式,是 source 内各个元素的单个 getter 结果。
  • source 是函数 function 类型
    • 如果有回调函数
      • getter 就是 source 函数执行的结果,这种情况一般是 watch api 中的数据源以函数的形式传入。
    • 如果没有回调函数,那么此时就是 watchEffect api 的场景了。
      • 此时会为 watchEffect 设置 getter 函数,getter 函数逻辑如下:
        • 如果组件实例已经卸载,则不执行,直接返回
        • 否则执行 cleanup 清除依赖
        • 执行 source 函数
  • 如果 source 不是以上的情况,则将 getter 设置为空函数,并且报出 source 不合法的警告⚠️。

相关代码如下,由于逻辑已经完整的一丝不落的在上面分析了,所以就容笔者偷个懒,不加注释了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (isRef(source)) { // ref 类型的数据源,更新 getter 与 forceTrigger
  getter = () => (source as Ref).value
  forceTrigger = !!(source as Ref)._shallow
} else if (isReactive(source)) { // reactive 类型的数据源,更新 getter 与 deep
  getter = () => source
  deep = true
} else if (isArray(source)) { // 多个数据源,更新 isMultiSource、forceTrigger、getter
  isMultiSource = true
  forceTrigger = source.some(isReactive)
  // getter 会以数组形式返回数组中数据源的值
  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,让数据源作为 getter 函数
    getter = () =>
      callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
  } else {
    // 没有回调即为 watchEffect 场景
    getter = () => {
      if (instance && instance.isUnmounted) {
        return
      }
      if (cleanup) {
        cleanup()
      }
      return callWithAsyncErrorHandling(
        source,
        instance,
        ErrorCodes.WATCH_CALLBACK,
        [onInvalidate]
      )
    }
  }
} else {
  // 其余情况 getter 为空函数,并发出警告
  getter = NOOP
  __DEV__ && warnInvalidSource(source)
}

接着会处理 watch 中的场景,当有回调,并且 deep 选项为 true 时,将使用 traverse 来包裹 getter 函数,对数据源中的每个属性递归遍历进行监听。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (cb && deep) {
  const baseGetter = getter
  getter = () => traverse(baseGetter())
}

之后会声明 cleanup 和 onInvalidate 函数,并在 onInvalidate 函数的执行过程中给 cleanup 函数赋值,当副作用函数执行一些异步的副作用,这些响应需要在其失效时清除,所以侦听副作用传入的函数可以接收一个 onInvalidate 函数作为入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时。
  • 侦听器被停止(如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let cleanup: () => void
let onInvalidate: InvalidateCbRegistrator = (fn: () => void) => {
  cleanup = runner.options.onStop = () => {
    callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
  }
}

接着会初始化 oldValue 并赋值。

然后声明一个 job 函数,这个函数最终会作为调度器中的回调函数传入,由于是一个闭包形式依赖外部作用域中的许多变量,所以会放在后面讲,避免出现还未声明的变量造成理解困难。

根据是否有回调函数,设置 job 的 allowRecurse 属性,这个设置很重要,能够让 job 作为一个观察者的回调这样调度器就能知道它允许调用自身。

接着声明一个 scheduler 的调度器对象,根据 flush 的传参来确定调度器的执行时机。

  • 当 flush 为 sync 同步时,直接将 job 赋值给 scheduler,这样这个调度器函数就会直接执行。
  • 当 flush 为 post 需要延迟执行时,将 job 传入 queuePostRenderEffect 中,这样 job 会被添加进一个延迟执行的队列中,这个队列会在组件被挂载后、更新的生命周期中执行。
  • 最后是 flush 为默认的 pre 优先执行的情况,这是调度器会区分组件是否已经挂载,副作用第一次调用时必须是在组件挂载之前,而挂载后则会被推入一个优先执行时机的队列中。

这一部分逻辑的源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 初始化 oldValue
let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE
const job: SchedulerJob = () => { /*暂时忽略逻辑*/ } // 声明一个 job 调度器任务,暂时不关注内部逻辑

// 重要:让调度器任务作为侦听器的回调以至于调度器能知道它可以被允许自己派发更新
job.allowRecurse = !!cb

let scheduler: ReactiveEffectOptions['scheduler'] // 声明一个调度器
if (flush === 'sync') {
  scheduler = job as any // 这个调度器函数会立即被执行
} else if (flush === 'post') {
  // 调度器会将任务推入一个延迟执行的队列中
  scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
} else {
    // 默认情况 'pre'
  scheduler = () => {
    if (!instance || instance.isMounted) {
      queuePreFlushCb(job)
    } else {
      // 在 pre 选型中,第一次调用必须发生在组件挂载之前
      // 所以这次调用是同步的
      job()
    }
  }
}

在处理完以上的调度器部分后,会开始创建副作用。

首先声明一个 runner 变量,它创建一个副作用并将之前处理好的 getter 函数作为副作用函数传入,并在副作用选项中设置了延迟调用,以及设置了对应的调度器。

并通过 recordInstanceBoundEffect 函数将该副作用函数加入组件实例的的 effects 属性中,好让组件在卸载时能够主动得停止这些副作用函数的执行。

接着会开始处理首次执行副作用函数。

  • 如果 watch 有回调函数
    • 如果 watch 设置了 immediate 选项,则立即执行 job 调度器任务。
    • 否则首次执行 runner 副作用,并将返回值赋值给 oldValue。
  • 如果 flush 的刷新时机是 post,则将 runner 放入延迟时机的队列中,等待组件挂载后执行。
  • 其余情况都直接首次执行 runner 副作用。

最后 doWatch 函数会返回一个函数,这个函数的作用是停止侦听,所以大家在使用时可以显式的为 watch、watchEffect 调用返回值以停止侦听。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 创建 runner 副作用
const runner = effect(getter, {
  lazy: true,
  onTrack,
  onTrigger,
  scheduler
})

// 将 runner 添加进 instance.effects 数组中
recordInstanceBoundEffect(runner, instance)

// 初始化调用副作用
if (cb) {
  if (immediate) {
    job() // 有回调函数且是 imeediate 选项的立即执行调度器任务
  } else {
    oldValue = runner() // 否则执行一次 runner,并将返回值赋值给 oldValue
  }
} else if (flush === 'post') {
    // 如果调用时机为 post,则推入延迟执行队列
  queuePostRenderEffect(runner, instance && instance.suspense)
} else {
  // 其余情况立即首次执行副作用
  runner()
}

// 返回一个函数,用以显式的结束侦听
return () => {
  stop(runner)
  if (instance) {
    remove(instance.effects!, runner)
  }
}

doWatch 函数到这里就全部运行完毕了,现在所有的变量已经声明完毕,尤其是最后声明的 runner 副作用。我们可以回过头看看被调用了多次的 job 中究竟做了什么。

调度器任务中做的事情逻辑比较清晰,首先会判断 runner 副作用是否被停用,如果已经被停用则立即返回,不再执行后续逻辑。

之后区分场景,通过是否存在回调函数判断是 watch api 调用还是 watchEffect api 调用。

如果是 watch api 调用,则会执行 runner 副作用,将其返回值赋值给 newValue,作为最新的值。如果是 deep 需要深度侦听,或者是 forceTrigger 需要强制更新,或者新旧值发生了改变,这三种情况都需要触发 cb 回调,通知侦听器发生了变化。在调用侦听器之前会先通过 cleanup 清除副作用,接着触发 cb 回调,将 newValue、oldValue、onInvalidate 三个参数传入回调。在回调触发后再去更新 oldValue 的值。

而如果没有 cb 回调函数,即为 watchEffect 的场景,此时调度器任务仅仅需要执行 runner 副作用函数就好。

job 调度器任务中的具体代码逻辑如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const job: SchedulerJob = () => {
  if (!runner.active) { // 如果副作用以停用则直接返回
    return
  }
  if (cb) {
    // watch(source, cb) 场景
    // 调用 runner 副作用获取最新的值 newValue
    const newValue = runner()
    // 如果是 deep 或 forceTrigger 或有值更新
    if (
      deep ||
      forceTrigger ||
      (isMultiSource
        ? (newValue as any[]).some((v, i) =>
            hasChanged(v, (oldValue as any[])[i])
          )
        : hasChanged(newValue, oldValue))
    ) {
      // 当回调再次执行前先清除副作用
      if (cleanup) {
        cleanup()
      }
      // 触发 watch api 的回调,并将 newValue、oldValue、onInvalidate 传入
      callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
        newValue,
        // 首次调用时,将 oldValue 的值设置为 undefined
        oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
        onInvalidate
      ])
      oldValue = newValue // 触发回调后,更新 oldValue
    }
  } else {
    // watchEffect 的场景,直接执行 runner
    runner()
  }
}

总结

在本文中,笔者给大家详细讲解了 Vue3 中提供的 watch、watchEffect 两个 api 的实现,并且在组件的 option 选项中的 watch,其实也是通过 doWatch 函数来完成侦听的。在讲解的过程中,我们发现 Vue3 中的侦听器也是通过副作用来实现的,所以理解侦听器之前需要先了解透彻副作用究竟做了什么。

我们看到 watch、watchEffect 的背后都是调用并返回 doWatch 函数,笔者拆解分析了 doWatch 函数,让读者能够清楚的知道 doWatch 每一行代码都做了什么,以便于当我们的侦听器不如自己预期的工作时,可以从细节之处分析原因,而不至于瞎猜瞎试。

最后,如果这篇文章能够帮助到你了解更了解 Vue3 中的 watch 的原理以及它的工作方式,希望能给本文点一个喜欢❤️。如果想继续追踪后续文章,也可以关注我的账号或 follow 我的 github,再次谢谢各位可爱的看官老爷。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
vue3中的watch原理你了解多少
本篇文章为学习笔记,都是一个字一个字敲出来的,如果有和视频重叠部分,侵权行为告知立马删除。
zhouzhouya
2023/10/26
5010
vue3.0 源码解析三 :watch和computed流程解析
之前我们分两个章节详细的介绍了vue3.0 数据相应原理,知道了用proxy代替Object.defineProperty 的利与弊,了解了依赖收集和派发更新的大致流程,知道了vue3.0响应式原理,这节我们一起研究vue3.0中的 watch 有那些变化。
用户6835371
2021/06/01
1.2K0
vue3.0 源码解析三 :watch和computed流程解析
[建议收藏] 你想知道的Vue3核心源码这里都有
写作不易,未经作者允许禁止以任何形式转载! Effect和Reactive effect作为Vue响应式原理中的核心,在Computed、Watch、Reactive中都有出现 主要和Reactive(Proxy)、track、trigger等函数配合实现收集依赖,触发依赖更新 Effect 副作用依赖函数 Track 依赖收集 Trigger 依赖触发 Effect effect可以被理解为一个副作用函数,被当做依赖收集,在响应式数据更新后被触发。 Vue的响应式API例如Computed、Watch都有
前端LeBron
2021/12/08
1.5K0
顺藤摸瓜:用单元测试读懂 vue3 watch 函数
在 Vue 3.x 的 Composition API 中,我们可以用近似 React Hooks 的方式组织代码的复用;ref/reactive/computed 等好用的响应式 API 函数可以摆脱组件的绑定,抽离出来被随处使用了。
江米小枣
2020/07/02
2.1K0
Vue3源码阅读笔记之数据响应式
总结:Vue3中的数据响应式实现是一个较为独立的实现,适合单独分析学习哈。上文是删除了部分支线逻辑的版本,只保留了主线逻辑,大家如果想看完整的实现,还是建议去读源码哦。
wanyicheng
2021/04/12
8070
Vue3响应系统设计-下
上面的这个代码会导致栈溢出 Uncaught RangeError: Maximum call stack size exceeded
韦东锏
2023/08/26
2700
Vue3响应系统设计-下
看不懂来打我!Vue3的watch是如何实现数据监听的
watch这个API大家都很熟悉,今天这篇文章欧阳来带你搞清楚Vue3的watch是如何实现对响应式数据进行监听的。注:本文使用的Vue版本为3.5.13。
前端欧阳
2024/11/27
3540
看不懂来打我!Vue3的watch是如何实现数据监听的
[ Vue ] vue 设计原理之响应式系统实现笔记( 二 )
执行调度的实质就是将更多的控制权交给用户,比方说在执行副作用函数的时候可以让用户特定的去处理一些方法,例如回顾上一节的代码执行一个自增同时输出 status 的方法:
GavinUI
2022/08/19
7530
[ Vue ] vue 设计原理之响应式系统实现笔记( 二 )
Vue3 watch 与 watchEffect
该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
程序员海军
2023/11/05
5110
vue中的计算属性和侦听器
计算属性是基于响应式数据进行计算得出的结果并被缓存的属性。在组件的模板中可以像数据属性一样使用,它由一个计算函数和它所依赖的数据组成,只有当所依赖的数据发生变化时,它才会重新计算属性的值。Vue.js 内部实现了缓存机制,因此计算属性只会在必要的时候才重新计算。这样能够提高 Vue.js 应用的性能,并且让代码更加简洁和易于维护。
九仞山
2023/10/14
5670
vue3 watch监听应用技巧
之前用过vue2中的watch监听,最近在学vue3,对比两个版本对于watch使用的不同之处做个总结,然后记录下vue3中watch中的具体使用方法和技巧
iwhao
2024/07/25
3140
掌握这些容易被忽略的Vue细节,轻松排查问题,省时省力!
v-bind 如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。
奋飛
2023/07/10
4420
掌握这些容易被忽略的Vue细节,轻松排查问题,省时省力!
初识 vue3的Composition API
Composition API 也叫组合式API, 是在vue3中新引入的一种API,vue2中已经有option API了,那为什么要新稿这么一套呢,其实主要原因是要解决vue2中的option API的在处理复杂组件逻辑的局限性,例如逻辑分散、代码复用性差、类型推断困难、组件组织混乱、响应式系统限制、模板逻辑复杂性、组件测试困难等问题。为此Composition API通过函数的方式来组织代码,使得逻辑更加模块化和可组合,这就变得很灵活。
前端车神
2024/07/01
3010
盘点Vue3 watch的一些关键时刻能够大显身手的功能
欧阳年底也要毕业了,建了一个面试交流群一起交流面试心得,以及分享内推信息。扫描文末的二维码加欧阳好友,还可以加入高质量vue源码交流群,这个群里也有不少面试官。
前端欧阳
2024/11/22
2760
盘点Vue3 watch的一些关键时刻能够大显身手的功能
vue3的Composition API
Composition API 也叫组合式API, 是在vue3中新引入的一种API,vue2中已经有option API了,那为什么要新稿这么一套呢,其实主要原因是要解决vue2中的option API的在处理复杂组件逻辑的局限性,例如逻辑分散、代码复用性差、类型推断困难、组件组织混乱、响应式系统限制、模板逻辑复杂性、组件测试困难等问题。为此Composition API通过函数的方式来组织代码,使得逻辑更加模块化和可组合,这就变得很灵活。
iwhao
2024/08/04
2410
管理数据必备;侦听器watch用法详解,vue2与vue3中watch的变化与差异
侦听器是一个在 Vue.js 框架中用于观察和响应数据变化的机制。侦听器允许开发者指定一个函数,这个函数会在特定数据变化时自动执行。Vue.js 使用侦听器来实现数据双向绑定和响应式更新。
watermelo37
2025/01/22
4780
管理数据必备;侦听器watch用法详解,vue2与vue3中watch的变化与差异
超全的Vue3文档【Vue2迁移Vue3】
链接:https://juejin.cn/post/6858558735695937544#heading-153
小丑同学
2021/03/25
3K0
常见经典vue面试题(面试必问)
如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速
bb_xiaxia1998
2022/12/14
1.1K0
前端系列12集-全局API,组合式API,选项式API的使用
The setup() hook serves as the entry point for Composition API usage in components in the following cases: 在以下情况下, setup() 钩子用作组件中 Composition API 使用的入口点:
达达前端
2023/10/08
8150
Vue设计与实现读后感-响应式系统实现-场景增强computed与watch(三)- 2
我之前业务代码index.ts只是为了方便我在浏览器调试,并不能成为我代码健壮性的一部分。
吴文周
2022/03/30
1.7K0
相关推荐
vue3中的watch原理你了解多少
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档