如何理解vue的computed?

普源精电DHO系列示波器选购指南:从学生党到工程师的完整对比 本文深入评测普源精电DHO系列示波器,从DHO800到DHO4000各型号的核心参数、功能特点及适用场景进行全面对比,帮助学生党和工程师根据实际需求选择最佳型号。重点分析带宽、采样率、存储深度等关键指标,并提供实用的选购建议和进阶使用技巧,帮助用户充分发挥示波器性能。 阅读详情

在这里插入图片描述

这道考察computed属性的题蛮有意思的。

不仅仅考察了computed,而且还考察了vue的依赖收集以及脏检查。

computed : {
    foo() {
        if(this.a>0){ return this.a}
        else { return this.b + this.c }
    }
}
data() {
    a: 1,
    b: 1,
    c: 1,
}

众所周知,首次a,b,c均为1时,foo()返回值为1。
以foo()返回值为1作为起始态,独立的执行下面以下3个操作,vue会如何计算foo呢?

  • 如果此时this.a = 0,foo()如何计算?
  • 如果此时this.b = 2,foo()如何计算?
  • 如果a的初始值为-1,执行this.a = 1,foo()如何计算?

目录

  • 执行表现
  • 源码分析
    • createComputedGetter
    • 关键的watcher.js
    • 关键的dep.js
  • 基于源码分析拆解执行表现
    • 初始化a,b,c均为1时,foo()如何计算?
    • 如果此时this.a = 0,foo()如何计算?
    • 如果此时this.b = 2,foo()如何计算?
    • 如果a的初始值为-1,执行this.a = 1,foo()如何计算?
  • 一句话总结

执行表现

  • 如果此时this.a = 0,foo()如何计算?
  • 如果此时this.b = 2,foo()如何计算?
  • 如果a的初始值为-1,执行this.a = 1,foo()如何计算?
如果此时this.a = 0,foo()如何计算?

foo()的返回值会从this.a变为this.b+this.c,2。
vue会重新执行一遍evaluate,得到返回值this.b+this.c。

如果此时this.b = 2,foo()如何计算?

foo()的返回值仍旧为this.a,1。
vue会跳过evaluate的阶段,直接得到返回值this.a。

如果a的初始值为-1,执行this.a = 1,foo()如何计算?

foo()的返回值会从this.b+this.c变为this.a。
vue会重新执行一遍evaluate,得到返回值this.a。

为什么会是这样的呢?是否执行evaluate的条件是什么?
为什么a的初始值为-1了也可以重新evaluate?

源码分析

对于this.b = 2,vue跳过evaluate阶段,直接得到返回值this.a,是如何优化的呢?
下面我们来看源码:
源码地址:state.js
computed相关的有三个非常重要的函数:

  • createComputedGetter(脏检查,依赖收集)
  • 关键的watcher.js(Watcher)
  • 关键的dep.js(依赖)

先来看看最最核心的代码

// 脏检查, 执行计算
 if (watcher.dirty) {
    watcher.evaluate()
  }
 // Dep更新依赖
 if (Dep.target) {
    watcher.depend()
 }

下面再看具体的源码

createComputedGetter
  • 脏检查, 重新计算 对__computedWatcher中的具体的属性做检查
  • Dep更新依赖
function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
     // 脏检查, 执行计算
      if (watcher.dirty) {
        watcher.evaluate()
      }
     // Dep更新依赖
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}
关键的watcher.js
  • get 收集依赖
  • udpate 更新dirty为true,从而可以重新evaluate
  • evaluate 重新获得computed属性的值
  • depend 通知dep收集依赖
  • addDep watcher订阅依赖
export default class Watcher {
  lazy: boolean;
  dirty: boolean;
  constructor (
  ) {
    this.dirty = this.lazy // for lazy watchers,dirty用于懒监听
    this.value = this.lazy? undefined: this.get() // Dep的target设置为foo watcher
  }
  // Evaluate the getter, and re-collect dependencies.
  get () {
    pushTarget(this)
    value = this.getter.call(vm, vm)
    return value;
  }
  update () {
    if (this.lazy) {
      this.dirty = true
    }
  }
  evaluate () {
    this.value = this.get()
    this.dirty = false
  }
  depend () {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }
  addDep (dep: Dep) {
    const id = dep.id
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
  }
}
关键的dep.js
  • addSub:添加订阅
  • depend: 添加依赖
  • notify:通知更新
export default class Dep {
  constructor () {
    this.subs = []
  }
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}
Dep.target = null

基于源码分析拆解执行表现

  • 初始化a,b,c均为1时,foo()如何计算?
  • 如果此时this.a = 0,foo()如何计算?
  • 如果此时this.b = 2,foo()如何计算?
  • 如果a的初始值为-1,执行this.a = 1,foo()如何计算?
computed : {
    foo() {
        if(this.a>0){ return this.a}
        else { return this.b + this.c }
    }
}
data() {
    a: 1,
    b: 1,
    c: 1,
}
created(){ this.b = 2; }
初始化a,b,c均为1时,foo()如何计算?
  • 初始化watcher
  • 创建getter得到value并将dirty置为false
  • watcher帮助dep收集依赖,收集的是this.a
  • 依赖收集图
初始化watcher
_computedWatchers:{
    foo: Watcher {(vm, getter, null, { lazy: true })}
}
// watcher
Watcher: { lazy: true, dirty: true, value: undefined, deps:[] }
创建getter得到value并将dirty置为false
// Watcher: { lazy: true, dirty: true, value: undefined }
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
     // 脏检查, 执行计算
      if (watcher.dirty) {
        watcher.evaluate() // 得到value,dirty置为false
      }
     // 返回this.a 1
      return watcher.value
}

// watcher.evaluate() 拆解
evaluate () {
    // 从foo的getter get()得到value:this.a 1
    this.value = this.get()
   // 将dirty变为false
    this.dirty = false
}

执行完毕后,结果为Watcher { lazy: true, dirty: false, value: this.a, deps: [Dep a]}

watcher帮助dep收集依赖
if (watcher) {
     // Dep更新依赖
      if (Dep.target) {
        watcher.depend()
      }
}
// watcher.depend() 拆解
depend () {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
}
// dep.depend()拆解
depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
}
// watcher.addDep拆解
  addDep (dep: Dep) {
    const id = dep.id
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
  }
// dep.addSub()拆解
addSub (sub: Watcher) {
    this.subs.push(sub)
}

最终结果为:
计算属性foo仅仅收集了this.a作为dep。没有收集b和c。
Watcher { lazy: true, dirty: false, value: this.a , deps: [Dep a]}

依赖收集图(dirty为false)

deps: [Dep a(1)]
image

如果此时this.a = 0,foo()如何计算?
  • 执行computedGetter触发watcher.evaluate()
  • 在收集了a的基础上,再次收集到b和c的依赖
  • 依赖收集图

当我们执行this.a = 0时,a的setter发出依赖更新,getter执行更新,dirty由false变为true。
由于dirty为true,所以执行evaluate,得到foo()的返回值this.b+this.c。

   // 发出依赖更新
  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
  // dirty由false变为true
  update () {
    if (this.lazy) {
      this.dirty = true
    }
  }
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
     // 脏检查, 执行计算
      if (watcher.dirty) {
        // 执行evaluate
        watcher.evaluate()
      }
     // Dep更新依赖
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
  evaluate () {
    this.value = this.get()
    this.dirty = false
  }
  get () {
    pushTarget(this)
    value = this.getter.call(vm, vm)
    return value;
  }
  // 收集b和c的依赖
  if (Dep.target) {
        watcher.depend()
  }

最终收集到的依赖为Watcher: { lazy: true, dirty: true, value: this.b+this.c , deps: [Dep a, Dep b, Dep c]}

依赖收集图(dirty为true)

deps: [Dep a(1), Dep b(2), Dep c(2)]
image

如果此时this.b = 2,foo()如何计算?
  • 执行computedGetter不会触发watcher.evaluate()
  • 依赖收集图
执行computedGetter不会触发watcher.evaluate()

为什么执行computedGetter不会触发watcher.evaluate()?

因为仅收集了this.a的依赖
当我们执行this.b = 2时,b的setter发出依赖更新,getter执行更新。
但是,由于我们初始化的条件仅仅将this.a作为计算属性foo的依赖,所以不会有任何变化。

// Watcher { lazy: true, dirty: false, value: this.a,deps: [Dep a(1) }
return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
     // 此时watcher的dirty为false
      if (watcher.dirty) {
        watcher.evaluate()
      }
      // 返回this.a的值 1
      return watcher.value
    }
}
依赖收集图(dirty为false)

deps: [Dep a(1)]
image

如果a的初始值为-1,执行this.a = 1,foo()如何计算?
  • 每次get都会被收集到依赖中,收集a,b,c依赖到deps
  • 由于this.a的依赖被收集到,因此可以直接通过this.a = 0触发更新
  • 依赖收集图

源码get()的注释:

Evaluate the getter, and re-collect dependencies.

computed : {
    foo() {
       // a的get()触发,收集到deps
        if(this.a>0){ return this.a}
       // b和c的get()触发,收集到deps
        else { return this.b + this.c }
    }
}
data() {
    a: -1,
    b: 1,
    c: 1,
}

如何收集的?

get () {
    pushTarget(this)
    value = this.getter.call(vm, vm)
}

此时再触发this.a=1,由于this.a的依赖被收集到,因此可以直接触发更新。
最终返回1。

依赖收集图(dirty为true)

deps: [Dep a(1), Dep a(2), Dep a(2)]
image

一句话总结

一个computed属性中,每个类似this.foo的调用,都会在get()中重新收集依赖。当依赖收集大于一次(不是一个)时,视为脏(dirty)计算属性,需要重新 evaluate再取值。对于干净的计算属性,不需重新执行evaluate,vue直接取值即可。

期待和大家交流,共同进步,欢迎大家加入我创建的与前端开发密切相关的技术讨论小组:

努力成为优秀前端工程师!

Statistics and Machine Learning Toolbox Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具 立即下载

相关推荐

DETRs with Collaborative Hybrid Assignments Training论文阅读与代码

在这篇论文中,作者观察到在DETR中将过少的 Query 分配为正样本,采用一对一的集合匹配,会导致对编码器输出的监督稀疏,严重损害编码器的区分特征学习,反之亦然,也会影响解码器中的注意力学习。为了缓解这个问题,作者提出了一种新颖的协同混合分配训练方案,名为Co-DETR,以从多样的标签分配方式中学习更高效、更有效的基于DETR的检测器。这种新的训练方案可以通过训练多个并行辅助 Head ,以一对多的标签分配方式(如ATSS和Faster RCNN)进行监督,轻松增强端到端检测器中编码器的学习能力。

qingjing0413的博客 1154

Vue computed 计算属性

将msg这个字符串 反向 输出 html代码 // 普通实现 {{ msg.split('').reverse().join('') }} // 计算属性实现 逻辑和数据分离 <p> {{ newMsg }} </p> js代码 new Vue({ el: '#app', data: { msg: 'I Love eat 葡萄 ' ...

weixin_41927760的博客 255

三维点云孔洞修复(附Python代码和公共点云数据集链接)

(1)物体自身缺失(2)物体自身形状复杂,很多凹凸面不够规则甚至存在空腔,空腔内部无法被三维激光扫描仪扫描到(3)物体部分区域对激光扫描不敏感,未被扫描上(4)扫描仪的扫描稳定性。

yanghl1215的博客 6952

Vue—关于computed(在计算属性中return函数的问题)

一、背景 项目中偶然遇到有人在computed中这样写代码: computed: { getName() { return function () { return "张三"; }; }, }, 这段代码看似没什么毛病,实际上它违背了computed的设计初衷,computed设计的原因是为了简化模板中又长又臭的计算逻辑,使模板代码看上去更加简洁,容易维护,并且计算属性会基于响应式依赖进行缓存,从而优化性能。 二、计算属性介绍 关于计算属性官方的描述如

weixin_37744697的博客 3959

scRNA-seq | 吐血整理的单细胞入门教程(从原理到代码实操)(二)

scRNA-seq | 吐血整理的单细胞入门教程(从原理到代码实操)(二)

m0_72224305的博客 4305

Starlink地面终端天线对星(一)对星流程,星历下载,信标信号

starlink卫星终端对星采用机械伺服+相控阵电子扫描的混合模式,兼顾快速盲扫和高精度跟踪;对星流程分三步:冷启动与盲捕获 → 建立初始链路并下载星历 → 进入正常通信模式;

chen2jiang3的博客 2723

vue data中的this指向问题

在data里定义Object类型的变量时,会发现Object中访问不到vue的this属性。 例如: export default { data(){ return { a: "123", b: { c: this.a } }; }, created() { console.log("b: ", this.b.c); // undefined } } 这种情况可以用到Object的get属性进行属性定义。

s4944315的博客 3564

通俗傻瓜式理清光栅化渲染和光线追踪渲染的原理

用通俗语言傻瓜式的讲清楚光栅渲染和光线追踪渲染的原理,方便没有相关理论基础的朋友理解入门。

qq_37366618的博客 2万+

vue的功能,没有计算属性computed的时候,是空白(这样说是不对的,把计算属性提出来,怎样的就要提出来呢

<!DOCTYPE html> <div id = "computed-basics"> <p>Has published books:</p> <span>{{ aoutor.books.length > 0 ? 'Yes' : 'No' }}</span> </div> </html> <script src="https://unpkg.com/vue@next"></script

weixin_40945354的博客 750

Android PMS——概述(一)

PMS(Package Manager Service)是 Android 操作系统的一个重要组件,它负责管理应用程序的包信息以及与应用程序的安装、卸载、权限控制等相关功能,确保应用程序的安全性和稳定性。 Android PMS 负责维护一个应用程序包的清单信息,该清单是一个 XML 文件,包含应用程序的各种元数据,例如应用程序的包名、版本号、权限要求、组件信息等。PMS通过访问该清单文件来获取应用程序的信息。 此外,Android PMS 还负责应用程序的安装和卸载。在安装应用

小旭的专栏 1666

YOLO26 实例分割技术:Mask 分支与特征融合

YOLO26实例分割技术研究摘要:本文提出了一种基于YOLO架构的高效实例分割方法,通过创新设计Mask分支和特征融合策略解决像素级分割精度、多尺度目标处理等挑战。系统采用共享Backbone提取特征,检测分支输出边界框和类别,Mask分支生成原型Mask和组合系数。核心创新包括:1)原型Mask生成与线性组合机制;2)检测与Mask特征共享;3)结合BCE和Dice的多任务损失函数。实验表明该方法在保持实时性的同时实现了高质量分割,为轻量级实例分割提供了新思路。

一键难忘的博客 248

vue computed 计算属性取对象的值,第一次报错undefined

代码如下: data() { return { limit:3, checkedIndex:0, addressList:[], isMdShow:false, addressId:"" }; }, components: { NavHeader, NavFooter, NavBre...

weixin_39818813的博客 1万+

【异常】安装hermes-agent时提示GnuTLS recv error (-110): The TLS connection was non-properly terminated.

Git克隆仓库时出现TLS连接异常报错的解决方案摘要: 当执行git clone命令时遇到"GnuTLS recv error (-110)"报错,通常由网络不稳定、缓冲区不足或证书验证问题导致。主要解决方案包括:1)增大Git HTTP缓冲区(git config --global http.postBuffer 524288000);2)使用GitHub镜像站加速克隆;3)临时禁用SSL验证进行测试;4)检查并调整代理配置;5)改用SSH协议克隆。建议优先尝试增大缓冲区和镜像站方案,

奶爸的编程之路,也就一周冷个三天 1350

【VueJS】深入理解 computed 和 methods 方法

前言   模板内的表达式是非常便利的,但是它们实际上只用于简单的运算。在模板中放入太多的逻辑会让模板过重且难以维护。例如: div id="example"> {{ message.split('').reverse().join('') }} div> computed 方法   所以引入了计算属性computed,将复杂的逻辑放入计算中进行处理,同时computed有缓

'Ablaze 的专栏 6270

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

computed vue 不 触发_Vue中的computed怎么理解?是如何实现的?

Vue中computed怎么理解?是如何实现的?computed 又称计算属性,什么情况下会用到计算属性,我们直接上代码理解一下// 我们有一个message,经过多次逻辑处理最终显示到页面中// 它不是一个简单的声明式逻辑,我们需要看一段时间才能理解message意图{{ message.split(' ').reverse().join(' ') }}// 所以,这时候应该尝试使用comput...

weixin_36481714的博客 1709

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

vue中computed与watch的理解

使用vue这个框架已经很久了,其间也遇到了很多的坑,在此记录一下,给自己一个总结,希望也能给大家带来一些启发(总结在文章末尾) 首先说一下computed, 定义:计算属性,对数据进行一些处理,筛选,但不影响原数据 eg: ```javascript <div id="v1"> <!--这里显示的是computed中的函数--> 共计:{{ total }} </div> <script src="vue.min.js"></scr

Chilamzxy的博客 224

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

对于vue中的computed属性的理解

1.最近正在学vue,看了网上很多资料,对于有vue中computed属性的解释都不是很清晰,跟同事聊起这个,对computed有了清晰的认识,下面有一部分文章是转自:https://www.w3cplus.com/vue/vue-computed-intro.html,感觉文章上面的礼通俗易懂,所以就借用此处的。 自己的理解: computed使用监控自己定义的变量,该变量不是data里...

起小点 3508

Vue2 - Computed 计算属性源码理解

这里主要通过视图渲染来理解 Computed 的执行流程,在视图内使用 Computed 属性是比较常见的场景。下文中的术语说明:Computed 创建的 Watcher 实例对象,负责计算 computed 属性:渲染视图的 Watcher 实例,视图中使用到 computed 属性时,会去读取 computed 属性的实际值初始化阶段创建 ComputedWatcher,定义响应式属性拦截器首次访问阶段:触发计算,收集依赖,建立响应式关系依赖更新阶段:依赖变化时标记为脏数据,下次访问时重新计算。

这个人很懒,有空就写文章 752

对vue计算属性computed的setter与getter的理解

大家都知道computed简单的使用方法,这儿只分享一下setter和getter用法: setter:设置值时触发。 getter:获取值时触发,与setter是没有必然联系的。 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></tit...

qq_32741973的博客 1762

Vue 中computed的简单理解

Vue 中computed的简单理解 基本理解 1.1 computed 中定义的变量 可以直接在页面调用, 在 computed定义的变量 不可以在data中再次定义 1.2 computed 中的数据会先缓存起来, 当依赖值发生变化的时候才会重新渲染 1.3 computed 中的数据也具有监听的作用 使用 computed: { } ......

小火车况且况且况且的博客 2484

computed set 自定义参数_完全理解Vue的渲染watcher、computed和user watcher

作者:Naicehttps://segmentfault.com/a/1190000023196603这篇文章将带大家全面理解vue的watcher、computed和user watcher,其实computed和user watcher都是基于Watcher来实现的,我们通过一个一个功能点去敲代码,让大家全面理解其中的实现原理和核心思想。所以这篇文章将实现以下这些功能点:实现数据响应...

weixin_32534943的博客 386

对 vue的computed的一些理解

首先说一下vue的响应式,数据模型仅仅是普通的js对象,当你修改它们的时候,视图就会更新。 当把一个普通js对象传给Vue实例的data,Vue将遍历对象所有的属性,使用Object.defineProperty将这些属性转为getter/setter, 在内部让Vue追踪依赖,在属性被访问或者修改的时候通知变化,每个组件实例都有watcher实例。它在组件渲染的途中把属性记为依赖,之后当依赖项的...

onepunchmen00的博客 4206

理解与使用,Vue中的computed属性

computed属性的使用 基本使用方法 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>computed</title> </head> <body> <div id="app"&...

Tomoyagu的博客 444

computed vue 不 触发_vue中关于computed的一点理解

var parent = Vue.extend( {props: {childrens: ''},template: '{{age}}',data: function() {return {name: '',age: 18};},computed: {age: function() {return this.childrens.age +10;}},created: function() {var...

weixin_39916549的博客 259

vue里面的inputnumber可以带单位嘛_完全理解vue的渲染watcher、computed和user watcher

❝作者:naice链接:https://juejin.im/post/5f0b336e6fb9a07ea55f0b73来源:掘金❞这篇文章将带大家全面理解vue的渲染watcher、computed和user watcher,其实computed和user watcher都是基于Watcher来实现的,我们通过一个一个功能点去敲代码,让大家全面理解其中的实现原理和核心思想。所以这篇文...

weixin_39839968的博客 1257

vue中关于computed的一点理解

computed相当于属性的一个实时计算,如果实时计算里关联了对象,那么当对象的某个值改变的时候,同事会出发实时计算。 例子: <body id="content"> <parent :childrens="childrens"></parent> </body> <!-- 这个测试主要想证明:...

anmizhan5851的博客 166
上一篇: HTTP之强缓存和协商缓存
下一篇: 如何理解vue中的v-bind?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

趁你还年轻233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值