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的双向数据绑定是通过数据劫持集合发布者订阅者模式来实现的 阅读详情

相关推荐

新能源场站必看:一次调频与AGC/AVC的协同控制逻辑解析(含典型拓扑图)

本文深度解析了新能源场站中一次调频与AGC/AVC的协同控制逻辑。针对电网频率与电压稳定需求,详细阐述了三者在响应时序、控制目标上的分工与配合,并重点剖析了以RCL-0923装置为核心的指令融合架构与典型拓扑,为场站实现高效、稳定的协同运行提供了清晰的实践指导。

brown的博客 1068

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

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

old brother stable 2095

京东联盟h5st(3.1)加密参数逆向实战与反爬对抗策略

本文详细解析了京东联盟h5st(3.1)加密参数的逆向分析与实战破解过程。通过接口分析定位关键加密函数,拆解h5st结构并还原其基于SHA256和HMAC-SHA256的加密逻辑,最终使用Python集成JavaScript环境成功复现加密算法,并分享了应对反爬的频率限制、IP封禁及算法升级等对抗策略。

weixin_29246195的博客 1740

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

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

Wang的专栏 1304

ROBOGUIDE教程:FANUC机器人X型焊枪气动点焊焊接

FANUC机器人X型焊枪气动点焊焊接虚拟仿真实现方法。

IndRobSim 4389

SystemVerilog新手实战指南:iverilog与VScode的完美组合

本文为SystemVerilog新手提供了一套高效入门方案,详细介绍了如何利用开源编译器iverilog与轻量编辑器VScode搭建学习环境。内容涵盖十分钟快速安装配置、首个工程从编码到查看波形的完整流程,并演示了动态数组和队列等SystemVerilog新特性的使用,旨在帮助初学者避开复杂商业工具,快速掌握数字电路设计与验证的核心技能。

f6g7h8i9j的博客 514

vue3源码分析之computed

vue3源码分析之computed

weixin_44922480的博客 852

logisim中,如何设计一个位于十字路口的交通信号灯控制系统?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值。

**My Coding Family** 1348

computed(vue3)源码解析

vue3, computed源码原理解析

weixin_46996561的博客 722

YOLOv11知识蒸馏,实现无损涨点,包含逻辑蒸馏和特征蒸馏中的不同蒸馏方法,适用专栏内的所有模型

知识蒸馏(Knowledge Distillation)是由 Hinton 等人在 2015 年提出来的,主要是用一个复杂的教师模型去指导一个简单的学生模型训练,这样能让学生模型在保持高精度的同时,还能减少计算量。本文便是讲解如何针对的原始模型以及改进后的模型进行知识蒸馏,轻松实现无损涨点。知识蒸馏的核心在于利用已训练好的复杂模型(教师模型)去指导一个更简单的模型(学生模型)进行训练,以此让学生模型在保留较高精度的同时,降低计算成本。特征蒸馏(Feature Distillation)是知识蒸馏的一种形式,

Limiiiing的博客 4963

windows中通过docker部署LocateAnything-3B模型

windows中通过docker部署LocateAnything-3B模型

yilvyangguang520的博客 485

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

系统安装(kuntaiR522 kvm安装)

鲲泰R522服务器kvm安装rocky

weixin_46396917的博客 1822

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

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

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

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

Echoyya、的博客 449

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

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

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

weixin_41997616的博客 592

深入 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、付费专栏及课程。

余额充值