【Vue3源码学习】— CH2.7 Computed: Vue 3 计算属性深入解析

Computed: Vue 3 计算属性深入解析

  • 1.计算属性的基本用法
  • 2. ComputedRefImpl 类深入解析
    • JavaScript 中的 getter 函数
  • 3. 计算属性的创建:computed 方法解析
    • 3.1 源码解析
    • 3.2 使用示例
  • 4. 计算属性的工作原理
  • 5. 手动实现简化的计算属性
  • 6. 结语

在 Vue 3 的响应式系统中,计算属性(computed)扮演着重要的角色。它们基于响应式依赖进行缓存,并仅在依赖项变化时重新计算。这意味着,计算属性能够提供高效的数据处理方式,因为只有当实际需要时计算属性的值才会更新。下面,我们将详细探讨 Vue 3 中计算属性的实现细节及其用法。

1.计算属性的基本用法

计算属性依赖于其他响应式数据,并且仅在这些依赖数据发生变化时才重新计算其值。这种机制保证了性能的优化,避免了不必要的计算。

import { reactive, computed } from "vue";const state = reactive({count: 1,
});const plusOne = computed(() => state.count + 1);console.log(plusOne.value); // 2
state.count++;
console.log(plusOne.value); // 3

2. ComputedRefImpl 类深入解析

ComputedRefImpl 类是计算属性在 Vue 3 中的实现基础。它负责把用户定义的 getter 函数封装成响应式引用,并且管理计算结果的缓存。该类的关键实现如下:

export class ComputedRefImpl<T> {//用于存储与此计算属性相关的依赖(副作用函数)。public dep?: Dep = undefined//用于存储计算属性的当前值private _value!: T//一个 ReactiveEffect 实例,用于封装计算属性的 getter 函数。这个副作用函数会在依赖的响应式数据变化时重新执行,以更新计算属性的值public readonly effect: ReactiveEffect<T>//内部标志,用于标记这个对象是一个 Ref 类型,并且指示其只读状态。public readonly __v_isRef = truepublic readonly [ReactiveFlags.IS_READONLY]: boolean = falsepublic _cacheable: boolean/*** Dev only*/_warnRecursive?: boolean/*** 构造函数接收四个参数:* getter: 用户定义的计算属性的获取函数。* _setter: 用户定义的计算属性的设置函数,用于允许计算属性被赋新值。* isReadonly: 表明这个计算属性是否是只读的。* isSSR: 标记是否在服务器端渲染环境中使用,影响是否缓存计算结果。*/constructor(private getter: ComputedGetter<T>,private readonly _setter: ComputedSetter<T>,isReadonly: boolean,isSSR: boolean,) {//ReactiveEffect 被用于封装 getter 函数,确保每当依赖的数据变化时,都能够自动重新计算值,并缓存结果以提高性能。this.effect = new ReactiveEffect(() => getter(this._value),() =>triggerRefValue(this,this.effect._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect? DirtyLevels.MaybeDirty_ComputedSideEffect: DirtyLevels.MaybeDirty,),)this.effect.computed = thisthis.effect.active = this._cacheable = !isSSRthis[ReactiveFlags.IS_READONLY] = isReadonly}/*** 当访问计算属性的 value 时,会执行这个 getter 函数。* 这个函数首先检查是否需要重新计算计算属性的值(基于缓存逻辑和依赖数据的变化)。* 如果需要,它会运行封装的 getter 函数来更新 _value。* 然后,它会注册当前活动的副作用函数为这个计算属性的依赖,以便将来数据变化时能触发更新*/get value() {// the computed ref may get wrapped by other proxies e.g. readonly() #3376//“获取当前计算属性实例(this)背后的原始对象,并将其赋值给 self 变量”const self = toRaw(this)if ((!self._cacheable || self.effect.dirty) &&hasChanged(self._value, (self._value = self.effect.run()!))) {triggerRefValue(self, DirtyLevels.Dirty)}trackRefValue(self)if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty_ComputedSideEffect) {if (__DEV__ && (__TEST__ || this._warnRecursive)) {warn(COMPUTED_SIDE_EFFECT_WARN, `\n\ngetter: `, this.getter)}triggerRefValue(self, DirtyLevels.MaybeDirty_ComputedSideEffect)}return self._value}set value(newValue: T) {this._setter(newValue)}// #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.xget _dirty() {return this.effect.dirty}set _dirty(v) {this.effect.dirty = v}// #endregion
}

这个类通过 ReactiveEffect 封装 getter 函数,使计算属性能够响应依赖数据的变化。同时,通过缓存机制保证了性能的优化。

JavaScript 中的 getter 函数

get value() {} 是 JavaScript 中的一个 getter 函数的写法,它是对象属性访问器的语法之一。Getter 函数允许你定义一个对象属性,该属性在被访问时会自动执行一个函数来返回值,而不是直接返回一个值。这使得在对象属性被访问时可以执行更复杂的操作或计算,而对于使用者来说,这种访问看起来就像访问一个普通属性一样。

const person = {firstName: "John",lastName: "Doe",get fullName() {return `${this.firstName} ${this.lastName}`;}
};console.log(person.fullName); // 输出: John Doe

3. 计算属性的创建:computed 方法解析

Vue 3 提供了 computed 函数,用于创建计算属性。这个函数既可以接受一个简单的 getter 函数,也可以接受一个包含 get 和 set 方法的对象,允许创建可读写的计算属性。

3.1 源码解析

/*** 接受一个 getter 函数作为参数。这个 getter 函数定义了计算属性的计算逻辑,* 当依赖的响应式数据变化时,这个函数会被重新执行来更新计算属性的值。* 在这种情况下,计算属性是只读的,尝试写入会导致警告(在开发模式下)。*/
export function computed<T>(getter: ComputedGetter<T>,debugOptions?: DebuggerOptions,
): ComputedRef<T>/*** 接受一个包含 get 和 set 方法的对象 options 作为参数* 这允许你创建一个可写的计算属性。get 方法定义了计算逻辑,和只读计算属性一样。* set 方法允许你自定义当尝试修改计算属性的值时的行为,这在需要基于计算属性的值反向更新其依赖的响应式数据时非常有用。*/
export function computed<T>(options: WritableComputedOptions<T>,debugOptions?: DebuggerOptions,
): WritableComputedRef<T>export function computed<T>(getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,debugOptions?: DebuggerOptions,isSSR = false,
) {let getter: ComputedGetter<T>let setter: ComputedSetter<T>const onlyGetter = isFunction(getterOrOptions)if (onlyGetter) {getter = getterOrOptionssetter = __DEV__? () => {warn('Write operation failed: computed value is readonly')}: NOOP} else {getter = getterOrOptions.getsetter = getterOrOptions.set}//使用 ComputedRefImpl 类来实际创建计算属性const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)if (__DEV__ && debugOptions && !isSSR) {cRef.effect.onTrack = debugOptions.onTrackcRef.effect.onTrigger = debugOptions.onTrigger}return cRef as any
}

3.2 使用示例

import { computed } from "vue";// 创建只读计算属性
const readOnlyComputed = computed(() => someReactiveData.value + 1);// 创建可写计算属性
const writableComputed = computed({get: () => someReactiveData.value + 1,set: (newValue) => { someReactiveData.value = newValue - 1; }
});

4. 计算属性的工作原理

计算属性背后的核心是其延迟计算和缓存机制。ComputedRefImpl 类中的 effect 通过跟踪响应式依赖自动管理这些逻辑,保证了数据的实时性和性能的优化。当依赖数据变化时,计算属性会重新计算;否则,将直接使用缓存的结果。

5. 手动实现简化的计算属性

理解计算属性的实现机制后,我们可以尝试手动实现一个简化版本,以加深对其原理的理解。


function computedManual(getter){const result  = ref();  // 用于存储计算属性的结果const runner = effect(getter,{lazy:true, // 让 effect 不会立即执行scheduler:()=>{// 当依赖变化时,重新计算并更新 result 的值result.value = runner();}})// 立即执行一次 effect,初始化 result 的值result.value = runner();return {// 返回一个具有 value 属性的对象,模拟 ComputedRef 接口get value(){return result.value;},// 提供一个停止响应式依赖更新的方法stop:()=>stop(runner)}
}// 使用示例
const count = ref(1);
const doubled = computedManual(() => count.value * 2);console.log(doubled.value); // 输出: 2
count.value = 2;
console.log(doubled.value); // 输出: 4

这个简化的实现利用了 Vue 的 effect 和 ref,通过设定 lazy 选项来控制副作用函数的执行,同时使用调度器更新计算结果。

6. 结语

计算属性是 Vue 3 响应式系统中不可或缺的一部分,它通过缓存和自动更新机制,有效地优化了数据处理的性能。通过深入理解其背后的实现原理,我们能更好地利用 Vue 提供的响应式功能构建高效的应用。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/587562.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Go】二十、反射

文章目录 1、反射2、对基本数据类型反射3、对结构体进行反射4、获取变量的类别5、通过反射修改基本类型变量的值6、通过反射操作结构体的属性和方法 1、反射 //核心包 import ("reflect")通过反射&#xff1a; 可以在运行时动态获取变量的类型、获取结构体的信息&a…

mac mini m1芯片 Xcode 15.3 各种报错的问题

错误一&#xff1a; /Users/mac/Desktop/Test_project/mobile-ios/Test/Test-Bridging-Header.h:4:9 failed to emit precompiled header /Users/mac/Library/Developer/Xcode/DerivedData/App-apvcgkuclncgfqdlzqcoffyaexos/Build/Intermediates.noindex/PrecompiledHeaders/…

谷粒商城实战(008 缓存)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第151p-第p157的内容 简介 数据库承担落盘&#xff08;持久化&#xff09;工作 拿map做缓存 这种是本地缓存&#xff0c;会有一些问题 分布…

LeetCode每日一题之专题一:双指针 ——移动零

移动零OJ链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 解法&#xff08;快排的思想&#xff1a;数组划分区间-数组分两块&#xff09;&#xff1a; 算法思路&#xff1a;在本题中&#xff0c;我们可以用一个 dest 指针来扫描整个数组…

Nginx 高级

文章目录 Nginx反向代理概念配置 负载均衡概念配置 动静分离概念配置 网关防盗链keepalivednginx跨域 Nginx 反向代理 概念 反向代理&#xff08;Reverse Proxy&#xff09;方式是指以代理服务器来接受internet上的连接请求&#xff0c;然后将请求转发给内部网络上的服务器&…

美联储,非必要,不降息

美联储“没必要、没空间、没动力”降息&#xff0c;也会尽量避免货币政策干扰大选&#xff0c;用“口头降息”代替实际调整是现实选择&#xff0c;市场降息预期将继续推迟和下调。 前言&#xff1a; 当前美国经济从各个方面看均并未表现出疲态——新增就业持续修复&#xff0c;…

JVM 内存溢出排查

说明&#xff1a;记录一次JVM内存溢出的排查过程&#xff1b; 场景 项目开发完成后&#xff0c;首次提交到测试环境。测试、产品同事反馈页面先是操作响应慢&#xff0c;抛出超时异常&#xff0c;最后直接无法使用。查看日志后得知是内存溢出。 重启服务后&#xff0c;我对前…

苹果开发者账号注册后生成开发证书和发布证书的流程解析

转载&#xff1a;注册苹果开发者账号的方法 在2020年以前&#xff0c;注册苹果开发者账号后&#xff0c;就可以生成证书。 但2020年后&#xff0c;因为注册苹果开发者账号需要使用Apple Developer app注册开发者账号&#xff0c;所以需要缴费才能创建ios证书了。 所以新政策出…

使用kubeadm工具搭建Kubernetes集群

本文目录 一、CentOS7最小化安装&#xff08;master&#xff09;1、下载ISO镜像2、安装3、进入centos安装界面4、安装最小化安装必要的一些工具 二、克隆虚拟机&#xff08;node1、node2&#xff09;三、基础配置1、节点规划——部署架构图2、防火墙和SElinux配置2、主机名和ho…

关于不同AR(增强现实)SDK(软件开发工具包)的汇总和特性描述

以下是每个AR SDK的核心内容概述: ARCore 开发者:Google支持平台:Android(部分设备不支持)功能:运动追踪、平面追踪、点云图、云锚点、光照估计、环境探针、人脸追踪、2D图片追踪、人物遮挡、射线测试。官网链接:ARCoreARKit 开发者:Apple支持平台:iOS(iPhone和iPad)…

2024年最新版FL Studio21.2.3 Build 4004 for Mac 版激活下载和图文激活教程

FL studio21中文别名水果编曲软件&#xff0c;是一款全能的音乐制作软件&#xff0c;包括编曲、录音、剪辑和混音等诸多功能&#xff0c;让你的电脑编程一个全能的录音室&#xff0c;它为您提供了一个集成的开发环境&#xff0c;使用起来非常简单有效&#xff0c;您的工作会变得…

合并两个单链表

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 但行前路&#xff0c;不负韶华&#…