Jinuss's blog Jinuss's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

前端可视化
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 概览

  • 响应式系统

  • runtime运行时

  • runtime-core

    • nextTick
    • watch和watchEffect
      • provide和inject
      • schedule调度器
      • directive自定义指令
    • compiler编译

    • 《Vue3源码》笔记
    • runtime-core
    东流
    2024-06-28
    目录

    watch和watchEffect

    # 概述

    watch 和watchEffect是 Vue3 中常用的两个 api,用于监听数据变化。除此之外还有 vue3 还提供了watchPostEffect和watchSyncEffect

    # watch

    • watch(source, cb, options?) watch主动监听,在初始化时会执行一次回调函数,当监听的数据source发生变化时会执行回调函数cb。cb接受两个参数,一个是newValue,一个是oldValue。 watch的source可以是一个getter函数,也可以是一个ref对象,也可以是一个reactive对象。可以同时监听多个数据,也存在一定的性能的开销。

    # watchEffect

    • watchEffect(effect, options?) watchEffect被动监听,在初始化时会执行一次回调函数,当回调函数内部中的任何响应式数据发生变化都会执行回调函数effect,它也无需指定具体的依赖。 effect接受一个参数,是onInvalidate,用于注册一个清理函数,当监听的数据发生变化时,会执行清理函数。

    # watchPostEffect

    • watchPostEffect(effect): 和watchEffect类似,自动监听,回调函数会在 DOM 更新之后调用,能够确保回调函数执行时可以获取到更新后的 DOM,适用于需要在 DOM 更新后执行操作的情况,如读取元素的位置、尺寸等信息

    # watchSyncEffect

    • watchSyncEffect(effect): 同步监听,每当依赖变化时立即触发回调函数,而不会等待下一次事件循环。适用于需要立即响应数据变化的情况,如实时计算或者紧急数据更新等

    # 四种监听器的实现

    vue3 内部实现这 4 种监听器的原理大同小异,都是返回调用doWatch函数,位于packages\runtime-core\src\apiWatch.ts

    # doWatch原理

    # 传参

    doWatch函数接受三个参数,分别是source、cb和options。

    • watch可以传入三个参数,options={immediate,deep,flush,once}
    • watchEffect传入两个参数,options={immediate,deep,flush,once}
    • 对于watchPostEffect和watchSyncEffect来说,只有source参数作为回调函数,watchPostEffect函数内部options是{flush:post};watchSyncEffect函数内部options是{flush:sync}

    # 函数内部逻辑

    首先doWatch会判断是否cb和once,如果有,就将其包装成一个新的函数,内部调用unwatch用于取消监听。

    if (cb && once) {
      const _cb = cb;
      cb = (...args) => {
        _cb(...args);
        unwatch();
      };
    }
    
    1
    2
    3
    4
    5
    6
    7

    紧接着就是判断source的类型,从而设置不同的getter函数,getter函数用于后面定义effect。同时还会判断是否是多源,设置isMultiSource的值,如果source是数组,则isMultiSource为true。如果是watch,且options种的deep为true,则为深度监听。

    后面doWatch定义了onClenup函数,用于注册一个清理函数,当监听的数据发生变化时,会执行清理函数。 doWatch内部还针对 SSR 做了单独处理,定义不同的清理函数。

    doWatch定义了一个SchedulerJob类的 job 函数,根据flush的值,定义不同的scheduler。由此可知只有watch和watchEffect的job会是会进入队列,调用queueJob。

      let scheduler: EffectScheduler
      if (flush === 'sync') {
        scheduler = job as any // the scheduler function gets called directly
      } else if (flush === 'post') {
        scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
      } else {
        job.pre = true
        if (instance) job.id = instance.uid
        scheduler = () => queueJob(job)
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    至此,我们可以拿到getter和scheduler,通过const effect = new reactivity.ReactiveEffect(getter, shared.NOOP, scheduler) 定义一个effect, effect是响应式里提到的一个概念,它会自动追踪其依赖,并在依赖变更时重新运行自身。

    然后,定义了unwatch函数作为doWatch的返回值,用于取消监听。其内部会调用effect.stop(),停止effect的运行。

    最后一部分就是doWatch的初始化运行

    • watch:当cb存在,若immediate为true,则直接运行job,否则调用effect.run()赋值给oldValue
    • watchPostEffect:会运行一次queuePostRenderEffect,将job放入队列中
    • watchEffet则和watchSyncEffect一样,直接运行effect.run()
    编辑 (opens new window)
    上次更新: 2024/12/03, 14:43:37
    nextTick
    provide和inject

    ← nextTick provide和inject→

    最近更新
    01
    GeoJSON
    05-08
    02
    Circle
    04-15
    03
    CircleMarker
    04-15
    更多文章>
    Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式