Vue中的computed和watch的原理解析

 更新时间:2025年12月01日 09:10:13   作者:BUG 饲养员  
Vue的computed和watch都是响应式系统的重要组成部分,computed通过依赖缓存提高性能,而watch用于监听数据变化并执行副作用,两者核心机制不同,而watch则是监听变化并触发回调,本文介绍Vue中的computed和watch的原理,感兴趣的朋友跟随小编一起看看吧

一、核心基础:响应式系统支撑

computed与watch的实现均依赖Vue响应式核心——依赖收集与触发更新

  • 响应式数据通过Object.defineProperty(Vue2)或Proxy(Vue3)劫持 getter/setter;
  • 读取数据时收集依赖(记录使用该数据的组件/计算属性);
  • 修改数据时触发setter,通知所有依赖执行更新逻辑。

二者本质是响应式系统的“上层应用”,差异在于对依赖变化的处理逻辑。

二、computed:带缓存的依赖推导

2.1 核心原理:缓存+懒执行

computed是“派生值”计算器,核心特性是依赖不变则复用缓存,避免重复计算:

  • 依赖收集:首次访问computed时,执行其getter函数,收集依赖的响应式数据;
  • 缓存结果:将计算结果缓存,标记为“清洁”状态;
  • 懒更新:仅当依赖数据变化时,才标记为“脏”状态,下次访问时重新计算并更新缓存。

2.2 简化实现代码

// 模拟computed核心逻辑(Vue3风格)
class ComputedRef {
  constructor(getter, ctx) {
    this.getter = getter; // 计算函数
    this.ctx = ctx; // 上下文
    this._value = null; // 缓存值
    this._dirty = true; // 脏标记:true需重新计算
  }
  // 访问computed时触发
  get value() {
    if (this._dirty) {
      this._value = this.getter.call(this.ctx); // 执行计算
      this._dirty = false; // 标记为清洁
    }
    return this._value; // 复用缓存
  }
  // 依赖变化时调用
  markDirty() {
    this._dirty = true; // 标记为脏
  }
}
// 实际使用
const count = ref(1);
// 计算属性:依赖count
const doubleCount = new ComputedRef(() => count.value * 2, this);
console.log(doubleCount.value); // 2(计算并缓存)
console.log(doubleCount.value); // 2(直接复用缓存)
count.value = 2; // 触发count的setter,调用doubleCount.markDirty()
console.log(doubleCount.value); // 4(重新计算)

三、watch:数据变化的副作用触发器

3.1 核心原理:监听变化+执行回调

watch是“数据监听器”,核心是监听指定数据源变化,触发自定义副作用(如异步请求、DOM操作),支持深度监听和立即执行。

3.2 关键特性与实现

// 模拟watch核心逻辑(Vue3风格)
function watch(source, callback, options = {}) {
  const { immediate = false, deep = false } = options;
  let oldValue;
  // 1. 初始化:获取初始值
  const getInitialValue = () => {
    // 深度监听时递归收集对象所有属性依赖
    if (deep && isObject(source.value)) {
      traverse(source.value);
    }
    return source.value;
  };
  // 2. 副作用函数:数据变化时执行
  const effectFn = () => {
    const newValue = source.value;
    callback(newValue, oldValue); // 触发回调
    oldValue = newValue;
  };
  // 3. 立即执行(immediate为true时)
  if (immediate) {
    oldValue = getInitialValue();
    effectFn();
  } else {
    oldValue = getInitialValue();
  }
  // 4. 关联响应式数据:依赖变化时触发effectFn
  effect(effectFn, { lazy: false });
}
// 实际使用
const user = reactive({ name: '张三', age: 20 });
// 监听user.age,支持深度监听
watch(
  () => user.age,
  (newAge, oldAge) => {
    console.log(`年龄从${oldAge}变为${newAge}`);
  },
  { immediate: true, deep: false }
);

深度监听(deep: true)通过递归遍历对象属性实现,会收集所有子属性依赖,性能消耗较高,非必要不使用。

四、核心差异与适用场景

维度computedwatch
核心机制依赖推导,缓存结果监听变化,触发副作用
缓存特性有缓存,依赖不变不计算无缓存,变化即触发
返回值必须返回值(派生数据)无需返回值(执行操作)
适用场景数据格式化、多值计算(如总价=单价×数量)异步操作、数据变化后的复杂逻辑(如刷新列表)

五、实战避坑要点

  • computed避免副作用:getter中不可执行异步操作或修改DOM,否则会破坏缓存逻辑和响应式追踪。
  • watch精简监听源:尽量监听具体属性(如()=>user.age)而非整个对象,减少深度监听的性能损耗。
  • Vue3注意点:watch监听reactive对象时,默认开启深度监听;监听ref时需通过.value访问。
  • 缓存失效场景:computed依赖非响应式数据(如普通变量)时,数据变化不会触发重新计算。

到此这篇关于Vue中的computed和watch的原理解析的文章就介绍到这了,更多相关vue computed和watch原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 实现一个 Vue 吸顶锚点组件方法

    实现一个 Vue 吸顶锚点组件方法

    这篇文章主要介绍了实现一个 Vue 吸顶锚点组件方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • Vue+Router+Element实现简易导航栏

    Vue+Router+Element实现简易导航栏

    这篇文章主要为大家详细介绍了Vue+Router+Element实现简易导航栏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • Vue.js中关于侦听器(watch)的高级用法示例

    Vue.js中关于侦听器(watch)的高级用法示例

    Vue.js 提供了一个方法 watch,它用于观察Vue实例上的数据变动。下面这篇文章主要给大家介绍了关于Vue.js中关于侦听器(watch)的高级用法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-05-05
  • Vue状态管理之使用Pinia代替Vuex

    Vue状态管理之使用Pinia代替Vuex

    这篇文章主要介绍了Vue状态管理。下面文章主要围绕着使用Pinia代替Vuex的相关资料展开具体内容,需要的朋友可以参考一下,希望对你有所帮助
    2021-11-11
  • vue-element-admin登录拦截设置白名单方式

    vue-element-admin登录拦截设置白名单方式

    这篇文章主要介绍了vue-element-admin登录拦截设置白名单方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • axios库的核心代码解析及总结

    axios库的核心代码解析及总结

    这篇博客针对axios库的核心代码做一个简要总结,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • rem实现响应式布局的思路详解

    rem实现响应式布局的思路详解

    这篇文章主要为大家介绍了rem实现响应式布局的思路详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • vue实现移动端触屏拖拽功能

    vue实现移动端触屏拖拽功能

    这篇文章主要为大家详细介绍了vue实现移动端触屏拖拽功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • Vue中fragment.js使用方法小结

    Vue中fragment.js使用方法小结

    这篇文章主要给大家汇总介绍了Vue中fragment.js使用方法的相关资料,需要的朋友可以参考下
    2020-02-02
  • 详解Vue中使用Echarts的两种方式

    详解Vue中使用Echarts的两种方式

    这篇文章主要介绍了Vue中使用Echarts的两种方式,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-07-07

最新评论