vue2/3 watch原理

watch

Vue2 watch 的工作流程

vue2 4.初始化数据流程中,new了一些Watcher(),watch就是这些watcher

  1. 初始化 :为每个 watch 项创建 Watcher 实例
  2. 依赖收集Watcher 首次执行 get(),触发响应式数据的 getter,将自身收集为依赖
  3. 深度监听 :如果设置了 deep: true,会递归遍历对象的所有属性,触发它们的 getter,从而收集所有嵌套属性的依赖
  4. 触发更新 :当响应式数据变化时,触发 setter,调用所有依赖的 update() 方法
  5. 执行回调Watcherupdate() 比较新旧值,如果变化则执行回调

在vue3中,watch,是对ReactiveEffect的另一种effect实现

Vue3 watch 的工作流程

vue3 四、创建副作用中,通过ReactiveEffect创建了effect,watch是对effect的更深度的实现

  1. 标准化源 :将各种形式的 source(ref、reactive、getter 函数、数组)统一为 getter 函数

  2. 深度监听处理 :如果启用深度监听,会递归遍历对象,触发所有属性的 getter,收集依赖

  3. 创建 effect :创建 ReactiveEffect,将 getter 作为副作用函数

  4. 依赖收集 :首次执行 effect.run(),触发 getter,读取响应式数据,完成依赖收集

  5. 调度更新 :当依赖的数据变化时,根据 flush 选项决定何时执行回调

    • 'pre':组件更新前执行(默认)
    • 'post':组件更新后执行(类似 Vue2 的 $nextTick
    • 'sync':同步执行
  6. 执行回调 :执行用户提供的回调函数,支持副作用清理

js 复制代码
// 简化版 watch 实现
function watch(source, cb, options = {}) {
  const {
    immediate = false,
    deep = false,
    flush = 'pre' // 'pre' | 'post' | 'sync'
  } = options;
  
  let getter;
  let cleanup;
  let oldValue;
  
  // 1. 标准化 source 为 getter 函数
  if (isRef(source)) {
    // ref 类型
    getter = () => source.value;
  } else if (isReactive(source)) {
    // reactive 对象
    getter = () => source;
    // reactive 对象默认深度监听
    deep = true;
  } else if (typeof source === 'function') {
    // getter 函数
    getter = source;
  } else if (Array.isArray(source)) {
    // 数组形式,监听多个源
    getter = () => source.map(s => {
      if (isRef(s)) return s.value;
      if (isReactive(s)) return traverse(s);
      if (typeof s === 'function') return s();
      return s;
    });
  } else {
    getter = () => {};
  }
  
  // 2. 深度监听处理
  if (deep) {
    const baseGetter = getter;
    getter = () => traverse(baseGetter());
  }
  
  // 3. 定义作业函数
  const job = () => {
    if (!effect.active) return;
    
    const newValue = effect.run();
    
    // 清理副作用
    if (cleanup) {
      cleanup();
    }
    
    // 执行回调,传入清理函数
    cb(newValue, oldValue, (fn) => {
      cleanup = () => {
        fn();
        cleanup = null;
      };
    });
    
    oldValue = newValue;
  };
  
  // 4. 创建调度器
  let scheduler;
  if (flush === 'sync') {
    scheduler = job;
  } else if (flush === 'post') {
    scheduler = () => queuePostFlushCb(job);
  } else { // 'pre'
    scheduler = () => queueJob(job);
  }
  
  // 5. 创建响应式 effect
  const effect = new ReactiveEffect(getter, scheduler);
  
  // 6. 初始化
  if (immediate) {
    job();
  } else {
    oldValue = effect.run();
  }
  
  // 7. 返回停止函数
  return () => {
    effect.stop();
    if (cleanup) cleanup();
  };
}

// 深度遍历(与 Vue2 类似,但使用 Proxy)
function traverse(value, seen = new Set()) {
  if (!isObject(value) || value === null || seen.has(value)) {
    return value;
  }
  
  seen.add(value);
  
  if (isRef(value)) {
    traverse(value.value, seen);
  } else if (Array.isArray(value)) {
    for (let i = 0; i < value.length; i++) {
      traverse(value[i], seen);
    }
  } else if (value instanceof Map) {
    value.forEach((val, key) => {
      traverse(val, seen);
    });
  } else if (value instanceof Set) {
    value.forEach(val => {
      traverse(val, seen);
    });
  } else {
    for (const key in value) {
      traverse(value[key], seen);
    }
  }
  
  return value;
}

watchEffect 的实现

Vue3 还提供了 watchEffect,自动追踪其同步执行期间访问的所有响应式依赖:

js 复制代码
function watchEffect(effect, options = {}) {
  return doWatch(effect, null, options);
}

function doWatch(source, cb, options = {}) {
  const { flush = 'pre' } = options;
  
  let getter;
  if (typeof source === 'function') {
    getter = source;
  }
  
  // 响应式 effect
  const runner = effect(getter, {
    lazy: true,
    scheduler: () => {
      if (cb) {
        // watch
        scheduler();
      } else {
        // watchEffect:直接重新运行 effect
        runner();
      }
    }
  });
  
  // 记录初始值(仅 watch 需要)
  let oldValue;
  if (cb) {
    oldValue = runner();
  } else {
    // watchEffect 立即执行
    runner();
  }
  
  return () => {
    runner.stop();
  };
}
特性 watch watchEffect
监听方式 显式声明监听的数据源 自动追踪回调函数中使用的响应式依赖
回调参数 接收(newValue, oldValue, onCleanup) 只接收onCleanup清理函数
初始执行 默认不立即执行,需设置immediate: true 立即执行一次
返回值 返回新值和旧值 没有返回值,只能访问当前值
依赖收集 基于传入的数据源 基于回调函数执行时访问的响应式数据
适用场景 需要新旧值对比、精确控制监听目标 逻辑组合、响应式副作用聚合

总结

Vue的watch是通过依赖收集建立监听关系,在响应式数据变化时自动触发回调执行的侦听机制。

相关推荐
_AaronWong10 小时前
Electron 实现仿豆包划词取词功能:从 AI 生成到落地踩坑记
前端·javascript·vue.js
cxxcode10 小时前
I/O 多路复用:从浏览器到 Linux 内核
前端
用户54330814419410 小时前
AI 时代,前端逆向的门槛已经低到离谱 — 以 Upwork 为例
前端
JarvanMo10 小时前
Flutter 版本的 material_ui 已经上架 pub.dev 啦!快来抢先体验吧。
前端
恋猫de小郭10 小时前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程
哀木10 小时前
给自己整一个 claude code,解锁编程新姿势
前端
程序员鱼皮11 小时前
GitHub 关注突破 2w,我总结了 10 个涨星涨粉技巧!
前端·后端·github
UrbanJazzerati11 小时前
Vue3 父子组件通信完全指南
前端·面试
是一碗螺丝粉11 小时前
5分钟上手LangChain.js:用DeepSeek给你的App加上AI能力
前端·人工智能·langchain
wuhen_n11 小时前
双端 Diff 算法详解
前端·javascript·vue.js