当前位置: 首页 > news >正文

Vue3源码reactivity响应式篇之computed计算属性

概述

vue3中,computed函数用于表示计算属性,有惰性求值、响应式追踪依赖的特点。本文将介绍computed的实现原理以及其机制细节。

源码解析

computed计算属性和computed方法、ComputedRefImpl类以及refreshComputed方法有关。

computed方法

computed暴露给外部的就是computed方法,其源码实现如下:

function computed(getterOrOptions, debugOptions, isSSR = false) {let getter;let setter;if (shared.isFunction(getterOrOptions)) {getter = getterOrOptions;} else {getter = getterOrOptions.get;setter = getterOrOptions.set;}const cRef = new ComputedRefImpl(getter, setter, isSSR);return cRef;
}

computed方法实现比较简单,需要关注参数getterOrOptionsisSSRisSSR默认为false,它在服务端渲染会传值为truedebugOptions用以在开发环境调试。

computed会先判断getterOrOptions是否是函数,若是函数,则将其赋值给getter;当然getterOptions也可以是一个包含getset方法的对象。computed方法返回的是ComputedRefImpl实例,一般我们读取计算属性的值也是读取它的返回值的.value

ComputedRefImpl

ComputedRefImpl用于构造一个计算属性。

ComputedRefImpl的源码实现如下:

class ComputedRefImpl {constructor(fn, setter, isSSR) {this.fn = getter; //计算函数this.setter = setter; // 设置函数(可选)this["_value"] = undefined; // 缓存的结果,计算属性的值this.dep = new Dep(this); // 依赖收集器(收集依赖此计算属性的副作用effect)this["__v_isRef"] = true; // 表示为ref类型this["__v_isReadonly"] = undefined; // 只读标记this.deps = undefined; //当前计算属性依赖的响应式集合对象链表头this.depsTail = undefined; //链表尾this.flags = 16; //状态标记this.globalVersion = globalVersion - 1;// 全局版本号,用于脏检查this.isSSR = undefined; //服务端渲染标记this.next = undefined; //用于在effect链表中指向下一个节点this.effect = this; // 指向自身this["__V_isReadonly"] = !setter; //若无setter,则表示计算属性是只读的this.isSSR = isSSR;//ssr标记赋值}// 依赖变更调用notify() {this.flags |= 16;if (!(this.flags & 8) && activeSub !== this) {batch(this, true);return true;}}// 计算属性值访问器get value() {const link = this.dep.track()refreshComputed(this);if (link) {link.version = this.dep.version;}return this._value;}// 计算属性值设置器set value(newValue) {if (this.setter) {this.setter(newValue)} else {warn("Write operation failed: computed value is readonly");}}
}

ComputedRefImpl中除了在构造器中定义了相关属性外,就是包含两个属性访问器函数和一个notify方法

notify

当计算属性依赖的响应式值发生变化时,会调用notify方法.notify方法会先设置this.flags标志位的值,将其第4位置为1,表示有更新请求;然后判断标志位的第3位是否为1并且当前激活订阅(副作用)是不是自身,若条件满足,则调用batch方法,将该计算属性添加到更新队列中,进行批量更新,最后返回true,表示更新已调度;若不满足条件,则返回false,表示更新被跳过。

getter

getter属性访问器,会在读取计算属性的值时触发。先进行依赖收集,追踪当前正在运行的effect,然后调用refreshComputed方法进行有条件性的重新计算,若当前计算属性被其他effect依赖,则更新依赖的版本,最后返回this._value

setter

setter属性设置,一般情况下计算属性只是只读的,若this.setter方法存在,则可以调用该方法设置计算属性的值。

refreshComputed方法

refreshComputed方法就是用于进行刷新计算属性的值,满足条件就重新进行计算,得到最新的计算属性的值。

refreshComputed的源码实现如下:

function refreshComputed(computed) {// 检查更新条件if (computed.flags & 4 && !(computed.flags & 16)) {return;}// 清除pending状态computed.flags & =-17;// 全局版本校验 避免重复计算if (computed.globalVersion === globalVersion) {return;}computed.globalVersion = globalVersion;// 缓存有效性检查 if (!computed.isSSR && computed.flags & 128 && (!computed.deps && !computed._dirty || !isDirty(computed))) {return;}// 标记计算状态computed.flags |= 2;const dep = computed.dep;const prevSub = activeSub;const prevShouldTrack = shouldTrack;activeSub = computed;shouldTrack = true;try {// 依赖收集准备prepareDeps(computed);// 执行计算函数const value = computed.fn(computed._value);// 值变化检测if (dep.version === 0 || hasChanged(value, computed._value)) {computed.flags |= 128; // 标记validcomputed._value = value;  dep.version++; // 触发依赖更新}} catch (err) {dep.version++;throw err;} finally {activeSub = prevSub;shouldTrack = prevShouldTrack;cleanupDep(computed); // 清理过期依赖computed.flags &= -3; // 清除computing状态}
}

refreshComputed方法会先检查flags的值,若是被移除或者没有更新请求,则直接返回;然后修改flags状态,清除pending状态;比较全局版本号和计算属性的版本号,若二者一样,则返回,避免是在计算属性中修改了响应式属性引起的重新计算;修改响应式的版本号;然后做缓存有效性的检查;若是脏数据,则返回;再次标记flags状态,表示是计算中;将当前effect计算属性切换为activeSub,修改shouldTracktrue,调用prepareDeps进行依赖收集,然后执行计算属性的fn,即传入computed的参数函数,得到新的value,比较计算属性的值是否发生改变,赋值this._value,并将其依赖dep的版本递增,如此会触发依赖该计算属性的副作用effect更新;最后恢复activeSubshouldTrack,清理过期依赖以及清除计算状态。

refreshComputed是计算属性computed的核心方法,依据一些规则判断需要执行fn,获取最新的value以及触发相关依赖。


文章转载自:

http://a9LLS2lM.hypng.cn
http://mNhAvpsw.hypng.cn
http://4QL7wMZS.hypng.cn
http://uuDvgfvv.hypng.cn
http://wKSYBENa.hypng.cn
http://UZBRwQVY.hypng.cn
http://Ea1C8boq.hypng.cn
http://zUxYx2fg.hypng.cn
http://uZchMhT3.hypng.cn
http://nciOeGFL.hypng.cn
http://wbhxwPBS.hypng.cn
http://ro76SlBn.hypng.cn
http://DEW1gYdY.hypng.cn
http://IcX2HVXK.hypng.cn
http://h7ZC9VOT.hypng.cn
http://NEr00eir.hypng.cn
http://QaCfNAix.hypng.cn
http://ttShdI7D.hypng.cn
http://xQv7YofM.hypng.cn
http://Za2MoNvI.hypng.cn
http://rcvjjylF.hypng.cn
http://09YesKij.hypng.cn
http://B9DVPRwl.hypng.cn
http://tsopWY4h.hypng.cn
http://jRhhI4Ij.hypng.cn
http://ANoyV1Yn.hypng.cn
http://Ot94oFJz.hypng.cn
http://yy5Fiu3r.hypng.cn
http://fCqdAdn5.hypng.cn
http://MiOX4LEe.hypng.cn
http://www.dtcms.com/a/375402.html

相关文章:

  • 微服务02
  • RPA的天花板真的到了吗?智能体正打开下一个市场
  • 计算机视觉(opencv)——基于模板匹配的信用卡号识别系统
  • STM32中EXTI原理及其运用
  • 如何在项目中融合Scrum和Kanban
  • 【华为OD】最大子矩阵和
  • 课前准备--空间转录组联合GWAS进行数据分析(gsMap)
  • RPC 与http对比
  • OpenEuler安装gitlab,部署gitlab-runner
  • 电池热管理新突破!《Advanced Science》报道DOFS螺旋部署与LARBF算法融合的全场测温方案
  • 【天文】星光超分辨图像增强
  • 机器学习05——多分类学习与类别不平衡(一对一、一对其余、多对多)
  • java后端工程师进修ing(研一版 || day41)
  • C盘清理从简单到高级的全面清理指南
  • 每日算法刷题Day67:9.9:leetcode bfs10道题,用时2h30min
  • PCL 基于法向量进行颜色插值赋色
  • 四数之和
  • MySql案例详解之事务
  • golang 语言核心
  • 【项目】在AUTODL上使用langchain实现《红楼梦》知识图谱和RAG混合检索(二)RAG部分
  • 安卓学习 之 贞布局FrameLayout
  • 【ISP】Charlite工具实操
  • IntelliJ IDEA断点调试全攻略
  • OceanBase存储过程基本使用
  • 使用 OBD 交互式部署单点OceanBase数据库
  • 内存管理这一块
  • 【深度学习新浪潮】什么是具身智能?
  • Linux tc 常用命令总结(网卡限速、延迟、丢包与整形)
  • Windows 命令行:路径末端的反斜杠
  • Shell脚本编程基本认识