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是通过依赖收集建立监听关系,在响应式数据变化时自动触发回调执行的侦听机制。

相关推荐
于慨21 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz21 小时前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
从前慢丶21 小时前
前端交互规范(Web 端)
前端
CHU7290351 天前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序
GISer_Jing1 天前
Page-agent MCP结构
前端·人工智能
王霸天1 天前
💥别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配
前端·vue.js·数据可视化
小领航1 天前
用 Three.js + Vue 3 打造炫酷的 3D 行政地图可视化组件
前端·github
@大迁世界1 天前
2026年React大洗牌:React Hooks 将迎来重大升级
前端·javascript·react.js·前端框架·ecmascript
PieroPc1 天前
一个功能强大的 Web 端标签设计和打印工具,支持服务器端直接打印到局域网打印机。Fastapi + html
前端·html·fastapi
悟空瞎说1 天前
深入 Vue3 响应式:为什么有的要加.value,有的不用?从设计到源码彻底讲透
前端·vue.js