vue3源码分析 -- computed

【Vue.js 3.0源码】响应式之计算属性computed 自我介绍:大家好,我是吉帅振的网络日志;微信公众号:吉帅振的网络日志;前端开发工程师,工作4年,去过上海、北京,经历创业公司,进过大厂,现在郑州敲代码。计算属性是 Vue.js 开发中一个非常实用的 API ,它允许用户定义一个计算方法,然后根据一些依赖的响应式数据计算出新值并返回。当依赖发生变化时,计算属性可以自动重新计算获取新值,所以使用起来非常方便。我们举个简单的例子: 从代码中可以看到,我们先使用 ref API 创建了一个响应式对象 count,然后使用 computed API 创建了另一个响应 阅读详情

computed计算属性是存在依赖关系,当依赖的值发生变化时计算属性也随之变化

案例

首先引入reactive、effect、computed三个函数,声明obj响应式数据和computedObj计算属性,接着执行effect函数,该函数传入了一个匿名函数进行computedObj的赋值,最后两秒后修改obj的name值

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../../dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script>
      const { reactive, effect, computed } = Vue
      // 创建响应式数据
      const obj = reactive({
        name: 'jc'
      })
      // 计算属性 触发 obj.name 的 get 行为
      const computedObj = computed(() => {
        return '姓名:' + obj.name
      })
      // effect 函数中 触发 计算属性的 get 行为
      effect(() => {
        document.querySelector('#app').innerHTML = computedObj.value
      })
      
      // 修改响应式数据的 name 值 触发 set 行为
      setTimeout(() => {
        obj.name = 'cc'
      }, 2000)
    </script>
  </body>
</html>

ComputedRefImpl 实例

computed函数定义在packages/reactivity/src/computed.ts文件下:

在这里插入图片描述

该函数接收 getterOrOptions 参数,即我们传入的匿名函数() => { return '姓名:' + obj.name }:

在这里插入图片描述

之后赋值给getter,setter我们可以理解为一个空函数,之后创建一个 ComputedRefImpl 实例,并将其返回,看下ComputedRefImpl构造函数:

export class ComputedRefImpl<T> {
  public dep?: Dep = undefined

  private _value!: T
  public readonly effect: ReactiveEffect<T>

  public readonly __v_isRef = true
  public readonly [ReactiveFlags.IS_READONLY]: boolean

  public _dirty = true // 脏变量 关键
  public _cacheable: boolean

  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean,
    isSSR: boolean
  ) {
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this) // dirty 为 false 时 触发依赖
      }
    })
    this.effect.computed = this
    this.effect.active = this._cacheable = !isSSR
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    const self = toRaw(this)
    trackRefValue(self) // 依赖收集
    if (self._dirty || !self._cacheable) {
      self._dirty = false
      self._value = self.effect.run()!
    }
    return self._value
  }

  set value(newValue: T) {
    this._setter(newValue)
  }
}

该构造函数会创建一个ReactiveEffect实例,这块逻辑就不再赘述,先看下返回的实例effect:

在这里插入图片描述

另外还需关心 ReactiveEffect 传入的第二个参数 scheduler,该构造函数在packages/reactivity/src/effect.ts文件下:

scheduler我们可以理解为一个调度器,这也是**computed** 核心所在,该逻辑会进行依赖触发

// 传入的参数
() => {
    if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this) // dirty 为 false 时 触发依赖
    }
}

// ReactiveEffect 构造函数
export class ReactiveEffect<T = any> {
  // 省略
  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null,
    scope?: EffectScope
  ) {
    recordEffectScope(this, scope)
  }
  // 省略
}  

ComputedRefImpl定义了一个_dirty脏变量,还定义了get value和set value两个方法,这也是和ref相同,赋值时需带上.value属性的原因

get value 会进行依赖收集,但是依赖触发并没有在 set value 中,而是在我们之前 ReactiveEffect 传入的第二个参数中

此时computed函数执行完毕返回ComputedRefImpl实例对象:

在这里插入图片描述

之后执行effect函数,进行赋值document.querySelector('#app').innerHTML = computedObj.value,从而触发computed的get value方法:

在这里插入图片描述

get/set

trackRefValue(self)进行依赖收集,该方法之前也讲到过。由于此时_dirty脏变量为true(默认为true),所以之后设置为 false ,再执行 self.effect.run() 进行赋值

effect.run()实际执行的是fn()方法,即computed传入的匿名函数() => { return '姓名:' + obj.name },effect函数执行完毕,页面呈现如下:

在这里插入图片描述

两秒后触发obj的setter行为,即执行createSetter方法进行trigger依赖触发(第一次),然后根据name属性获取到对应的effects,该逻辑都在packages/reactivity/src/effect.ts文件下:

在这里插入图片描述

之后triggerEffects函数遍历effects,执行triggerEffect(effect, debuggerEventExtraInfo):

在这里插入图片描述

这里需要关注下if (effect.scheduler)判断逻辑,由于此时执行的**effect** 含有 computed 属性,且存在 scheduler,则会执行effect.scheduler()方法:

在这里插入图片描述

这就是之前提到的ComputedRefImpl构造函数中,创建ReactiveEffect实例时传入的第二个参数:

export class ComputedRefImpl<T> {
  // 省略
  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean,
    isSSR: boolean
  ) {
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this) // dirty 为 false 时 触发依赖
      }
    })
   // 省略
  }

  // 省略
}

// 第二个参数
() => {
    if (!this._dirty) {
        this._dirty = true
        triggerRefValue(this) // dirty 为 false 时 触发依赖
    }
}

因为在computed函数的get value方法中,_dirty设置了false,所以直接走判断逻辑,将 _dirty 设置为 false ,执行 triggerRefValue(this) 依赖触发(第二次) ,所以computed的依赖触发是在该逻辑中执行的,这里是关键

此时获取到的effects:

在这里插入图片描述

所以根据判断逻辑直接走 effect.run() ,执行run等于执行fn方法,即执行effect传入的匿名函数,之后执行document.querySelector('#app').innerHTML = computedObj.value赋值操作,再次触发computed的get value方法:

get value() {
    // the computed ref may get wrapped by other proxies e.g. readonly() #3376
    const self = toRaw(this)
    trackRefValue(self) // 依赖收集
    if (self._dirty || !self._cacheable) {
      self._dirty = false
      self._value = self.effect.run()!
    }
    return self._value
}

接着执行self._value = self.effect.run()!,又再次执行computed传入的匿名函数() => { return '姓名:' + obj.name }重新赋值:

在这里插入图片描述

代码执行完成,此时页面呈现修改后的值:

在这里插入图片描述

总结

1. computed 计算属性实际是一个 ComputedRefImpl 构造函数的实例

ComputedRefImpl构造函数中通过_dirty变量来控制effect中run方法的执行和triggerRefValue的触发

  • 初始时_dirty为true,表示计算属性的值尚未计算,需要重新计算
  • 当值被计算并缓存后,_dirty设置为false
  • 当计算属性的依赖发生变化时,例如响应式数据被修改,_dirty重新设置为true,表示需要重新计算值

2.想要访问计算属性的值,必须通过 .value ,因为它内部和 ref 一样是通过 get value 来进行实现的

每次.value时都会执行get value方法,从而触发trackRefValue进行依赖收集

3. 在依赖触发时,先触发**computed的effect** ,再触发非**computed的effect**

1)避免依赖循环和死锁

当计算属性和普通 effect 之间存在双向依赖时,若同时触发两者的更新,可能导致无限循环。例如:计算属性 A 的更新触发了普通 effect B,B 的修改又触发了 A 的依赖更新

先计算属性,后普通 effect 可以打破这种循环,计算属性的更新完成后,普通 effect 再基于最新的计算值执行,从而避免循环

2)确保计算属性的值是最新状态

计算属性的核心是缓存机制,其值需要基于依赖项的最新状态计算。若普通 effect 先执行,可能导致计算属性在后续访问时仍使用旧的缓存值(即脏状态未更新),从而产生错误结果

3)性能优化

计算属性的更新通常涉及复杂计算,而普通 effect 可能包含 DOM 操作等耗时任务。通过分阶段触发:

  • 减少重复计算:若普通 effect 先触发并修改了响应式数据,可能导致计算属性多次重新计算
  • 合并更新:计算属性的更新完成后,普通 effect 可以基于最终状态一次性完成渲染,减少重复渲染
前端面经-Vue 1、双向绑定原理,手撕简单源码(vue响应式原理) Vue的双向数据绑定是通过数据劫持集合发布者订阅者模式来实现的 阅读详情

相关推荐

CSP-J 初赛模拟卷1 题解

只有一棵子树有后继,其他的都是叶结点才能保证树的深度最大,此时还剩(n-5)个结点,最后加上2,得到(n-3),即为答案。题解:先修改p的指针域,让它的左指针域指向q,然后再让q的右指针域赋值给p的右指针域,再然后修改q的右指针域使其指向p,最后需要改变的指针域就是原先q结点的后继节点的左指针域内容。设n个元素的入栈序列是1,2,3,...,n,出栈序列为p1,p2,...,pn,若p1=n,则pi(1≤i≤n)的值为(1)输入的三个数中,第二个数应是[0,11]上的整数,否则会出现数组越界的问题。

Sat77GryHp_Pro的博客 2805

vue3 源码解析(3)— computed 计算属性的实现

通过对computed的源码分析,我们可以看到 vue3 如何实现计算属性的。当我们创建一个计算属性时,vue 会收集这个计算属性的所有依赖项。然后,当这些依赖项发生变化时,vue 会重新计算这个计算属性的值。并通过_dirty这个标志位来判断是否需要执行副作用函数并更新_value的值。这种机制使得我们可以方便地创建基于其他响应式依赖项的计算属性。

old brother stable 2095

科普:Frenet坐标

理想情况下,描述这种常见驾驶行为在数学上应该很容易。一种方法是使用新的坐标系。现在,而不是放下一个正常的笛卡尔网格,我们做一些事情,就像你看到下面…Frenet Coordinates是一种以比传统x,y笛卡尔坐标更直观的方式表示道路位置的方式。如果我们想要方程式来描述这个动作,那就不容易了!想象一下像下面这样一条弯曲的道路,笛卡尔坐标系就位于它的上面…使用这些笛卡尔坐标,我们可以尝试描述车辆通常在路上行驶的路径…那么在Frenet坐标系下呈现的典型轨迹是什么样的?在这里,我们定义了一个新的坐标系统。

OceanStar的博客 1万+

Vue3源码梳理:computed计算属性的核心实现

计算属性 computed 会基于其响应式依赖被缓存,并且在依赖的响应式数据发生变化时,重新计算测试示例: computed.html,测试vue源码的属性name : '张三' }) const computedObj = computed(() => {return '姓名: ' + obj . value . name }) effect(() => {clearTimeout(timer) obj . value . name = '李四' } , 2000)

Wang的专栏 1304

Figure 03人形机器人与Helix端到端控制系统:具身智能突破深度解析

2026年5月,Figure AI的Figure 03人形机器人在一场震撼业界的直播中完成了长达200小时的连续全自动作业,分拣近25万个包裹,零故障。这一里程碑式的成就标志着人形机器人正式从「实验室展示」迈入「规模化商用」阶段。本文深入剖析Figure 03的核心技术——Helix端到端神经网络控制系统,包括System 0/1/2三层架构、视觉运动策略、全身协同控制等关键技术,并提供完整的Python/Go代码示例,帮助开发者理解具身智能的核心原理与实现路径。关键词。

shao.bing的专栏 998

一文读懂AUTOSAR CAN通信

在AUTOSAR的分层和模块化架构中,使用PduR(Protocal Data Unit Router)将总线或者网络数据进行抽象,使得上层应用不用关心信号的来源。本文从PduR下层的CAN Interface、CAN Driver和CAN Transceiver Driver模块介绍CAN总线在AUTOSAR架构中的工作机制以及信号的收发等功能。PduR以及COM等上层模块的介绍可以参考公众号内发布的历史文章。

酒无忧的博客 2496

vue3源码分析之computed

vue3源码分析之computed

weixin_44922480的博客 852

做了一个3DTiles编辑器,支持3DTiles的预览和裁剪导出

3DTiles编辑器的功能极其简洁,核心就是3DTiles格式模型的预览和裁剪。为了演示方便,本文所使用的源数据为mipmap3d[1]网站所提供的无人机拍摄影像,并进行3DTiles构建。打开软件,输入的绝对路径之后,点击添加模型,就可以将模型加载进预览区。之后,点击裁剪模式按钮,默认会出现立方体裁剪框,可通过拖动句柄的方式去调整裁剪框的范围。同时,除了默认的盒体裁切模式外,还支持多边形裁切,点击多边形裁切后,可切换到顶视图,选择重绘多边形,可进行打点绘制。

兴趣使然的创作者 604

computed(vue3)源码解析

vue3, computed源码原理解析

weixin_46996561的博客 722

在 SAP 中,寄售业务(Consignment)与管道业务(Pipeline)都属于“物料先到、发票后到”的特殊采购模式,但两者的库存形态、物权转移时点、结算触发条件、系统配置完全不同

SAP中寄售业务与管道业务均属"先货后票"采购模式,但存在本质差异。寄售业务(如手机芯片)采用实物库存管理(标记"K"),物权在领用时转移,通过411K/201K移动类型触发负债;管道业务(如蒸汽)无实物库存,采用261P等移动类型直接消耗并生成应付。系统配置上,寄售需K类信息记录及GR收货,管道需P类信息记录且免GR。两者均通过MRKO汇总结算,但寄售按领用量计费,管道按提取量实时计费。核心区别在于库存形态与物权转移时点。

金牌架构师 558

置顶必读(1) |《YOLOv11实战:从入门到深度优化》专栏导读,带你一文了解本专栏!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏,该专栏专注于分享我在YOLOv8目标检测模型中的实战经验,涵盖从入门基础到深度优化的全方位解决方案。无论你是计算机视觉的新手,还是具有多年深度学习经验的专家,本专栏都将为你提供高效、可复现的技术方案,助你掌握YOLOv8的核心技术,提升目标检测性能,突破瓶颈,优化模型,并加速项目开发🚀!

**My Coding Family** 12万+

v3的一些用法

路由的使用 - computed 计算属性 – watch( ()=>{} )-- toRefs – onMounted import { computed, onMounted, reactive ,toRefs ,watch} from 'vue'; import { useRoute } from 'vue-router'; setup(props, context ){ // 默认执行一次 setup启动的意思 在created 之前 拿不到this实例 console.log

csstmg的博客 1041

mapbox高阶,加载倾斜摄影

本文详细介绍如何基于mapbox-gl v3.8.0版本实现倾斜摄影的加载,亲测可用。希望能帮助到您。一起学习,加油!加油!

我哎GIS博客 348

Vue 源码分析step_3-watch 和 computed

watch 的实现 Vue.prototype.$watch = function ( expOrFn, cb, options ) { const watcher = new Watcher(vm, expOrFn, cb, options) return function unwatchFn() { watcher.teardown() } } watch 的实现其实是把第一个参数设置成了响应式对象,watcher 构造函数

qq_41206257的博客 398

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Vue3 之 computed 计算属性的使用与源码分析详细注释

目录计算属性的基本用法计算属性的源码shared 工具方法抽离计算属性的基本用法 computed 一般有两种常见的用法: 一:传入一个对象,内部有 set 和 get 方法,属于ComputedOptions形式。在内部会有getter / setter两个变量来进行保存. const age = ref(18); const myAge = computed({ get() {}, ...

Echoyya、的博客 449

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

《Vue3 从入门到大神36篇》Vue3 源码详解(六):computed 源码分析——懒计算与缓存机制的实现

Vue3的computed实现机制解析(150字摘要) computed是Vue3响应式系统的核心功能,其本质是通过ComputedRefImpl类实现的带缓存effect。关键设计包含:1) lazy机制-首次不执行;2) dirty标记-依赖变化时仅标记不计算;3) 双层依赖系统-内部track响应式数据,外部track组件依赖。当访问.value时,若dirty为true才会重新计算并缓存结果,否则直接返回缓存值。这种设计实现了"惰性更新"哲学,确保衍生状态只在必要时重新计算,避免不必要的性能消耗。

weixin_41997616的博客 592

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

深入 Vue3 computed

本文解析Vue computed属性的实现机制,其核心是"惰性求值+依赖收集+脏检查缓存"的三重设计。通过dirty标志位控制缓存更新,仅在首次读取或依赖变化时重新计算;利用effect包裹getter实现精准依赖追踪;拒绝异步以保证数据一致性。文章还分析了可写computed的双通道实现,并指出异步场景应使用watch替代。computed的同步缓存特性是Vue性能优化的重要设计。

前端小巷子的博客 714

vue3 computed计算属性(源码解析)

【代码】vue3 computed计算属性(源码解析)

王帅鸣的博客 424

bootstrap响应式页面源码_Vue3 源码分析(1):响应式原理

本文首发于我的博客ahabhgk's blog​ahabhgk.github.io首先一起实现一个简易的 reactivity 吧,穿插着会提到源码中的一些细节,建议先跟着写写,细节可以 clone 下来 vue-next 打开源码跟着看⭐️ reactive首先是 reactive,它的作用是把一个对象变成响应式对象,更准确的说是 Object, Array, Map, WeakMap, Set...

weixin_35880151的博客 552

【mini-vue】Reactive模块学习笔记

ReactiveEffect类 RefImpl类 ComputedRefImpl类 activeEffect公共变量 shouldTrack公共变量 reactive函数 ref函数 computed函数 readonly函数 shallowReadonly函数 effect函数 targetMap公共变量 track函数 trigger函数 stop函数 isReactive函数 isReadonly函数 isProxy函数 isRef函数 unRef函数 proxyRef函数......

qq_53512708的博客 1350

vue3源码系列之计算属性computed原理剖析

前言 vue3诸多API中computed的作用毋庸置疑,也算是我们开发中使用比较多的API了。今天就让我们来探究下computed底层到底做了什么。如果想要学好computed个人觉得还是需要将effect有个深入的理解。这样学的东西才能融会贯通。 为什么一定强调学习effect呢?因为无论是watch,computed都是以effect为基础,所以说基石很重要,如果大家看源码实在很吃力的话,个人觉得看懂我写的就够了。因为我就是按照源码一比一写出来的 讲述方式: 基本使用 手写源码 源码对照

weixin_42373116的博客 930

面试官:请你说一下Vue中v-if和v-for的优先级谁更高吧?

l是vue的列表渲染函数,作用就是循环处理users的,第6行就是嵌套在_l函数中的,那这个结果就是每循环一次就去执行里面的函数一次,我们看到最后的结果就是return后面的,也就是说,-l函数包含了user.age>18的判断,也就是:每循环一次列表,就会判断一次user.age>18。每年的金三银四,都会有很多人都跃跃欲试了,但是再怎么着急也要先保证自己的技术达标,这样才能从容应对各种考验,这里我们就开始复盘Vue中常见的面试问题,看看自己都掌握了嘛~v-if与v-for都是vue模板系统中的指令。

zjjcchina的博客 414

深入源码设计!Vue3.js核心API——Computed实现原理

我们都知道computed是Vue3的响应式系统核心API,那么它是如何设计和实现的呢?相信阅读完这篇文章,你就能够手写computed了!

海风依旧 1022

前端面试怎么总问watch和computed区别

当你访问数据时,Vue会建立一个依赖关系,然后在数据发生变化时通知相关的依赖项,从而更新视图。的内部,依赖项追踪和回调触发是通过Vue的响应式系统实现的。计算函数的执行和结果的缓存是通过Vue的响应式系统实现的。在现代前端的面试中,vue和react是面试过程中基本必问的技术栈,其中在聊到Vue响应式话题时,在获取属性值时,Vue会清除先前的依赖项,然后重新追踪新的依赖项。的核心,它建立了对监视数据的依赖,并在数据变化时触发回调函数。用于派生出一个新的计算属性,它的值会根据依赖项的变化而变化。

菜菜程序员 461

vue 源码分析,实现原理(Watcher, Dep, computed)

把这里的代码复制到本地执行,看懂这100行代码,你就一定能看懂 vue const data = { flag: false, firstName: 'Zheng', lastName: 'Yimeng' } const computed = { name() { if (!data.flag) { return '你拿不到' } return data.firstName + ' ' + data.last.

左不言的博客 1181

快速进阶Vue3.0

在2019.10.5日发布了Vue3.0预览版源码,但是预计最早需要等到 2020 年第一季度才有可能发布 3.0 正式版。 可以直接看github源码。 新版Vue 3.0计划并已实现的主要架构改进和新功能: 编译器(Compiler) 使用模块化架构 优化 "Block tree" 更激进的 static tree hoisting 功能 (检测静态语法,进行提升) ...

hsany330的专栏 265
上一篇: 微前端简介、注意事项及常见技术方案
下一篇: vue3源码分析 -- watch
ZoeLandia
博客等级 码龄6年 734粉丝 183原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值