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

news2024/12/23 10:43:20

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 = true
    public readonly [ReactiveFlags.IS_READONLY]: boolean = false

    public _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 = this
        this.effect.active = this._cacheable = !isSSR
        this[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.x
    get _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 = getterOrOptions
        setter = __DEV__
            ? () => {
                warn('Write operation failed: computed value is readonly')
            }
            : NOOP
    } else {
        getter = getterOrOptions.get
        setter = getterOrOptions.set
    }

    //使用 ComputedRefImpl 类来实际创建计算属性
    const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)

    if (__DEV__ && debugOptions && !isSSR) {
        cRef.effect.onTrack = debugOptions.onTrack
        cRef.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.coloradmin.cn/o/1564398.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go之web框架gin

介绍 Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API&#xff0c;性能要好得多&#xff0c;多亏了 httprouter&#xff0c;速度提高了 40 倍。 如果您需要性能和良好的生产力&#xff0c;您一定会喜欢 Gin。 安装 go get -u github.com/gin-gonic/g…

XRDP登录ubuntu桌面闪退问题

修改 /etc/xrdp/startwm.sh unset DBUS_SESSION_BUS_ADDRESS unset XDG_RUNTIME_DIR . $HOME/.profile

【C语言】——指针七:数组和指针试题解析

【C语言】——指针七&#xff1a; 前言一、 s i z e o f sizeof sizeof 与 s t r l e n strlen strlen 的对比1.1、 s i z e o f sizeof sizeof1.2、 s t r l e n strlen strlen1.3、 s i z e o f sizeof sizeof 和 s t r l e n strlen strlen 对比 二、数组和指针笔试题解析…

C++之优化Linux内核结构体用智能指针std::unique_ptr与std::make_unique分配内存总结(二百六十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

自贡市第一人民医院:超融合与 SKS 承载 HIS 等核心业务应用,加速国产化与云原生转型

自贡市第一人民医院始建于 1908 年&#xff0c;现已发展成为集医疗、科研、教学、预防、公共卫生应急处置为一体的三级甲等综合公立医院。医院建有“全国综合医院中医药工作示范单位”等 8 个国家级基地&#xff0c;建成高级卒中中心、胸痛中心等 6 个国家级中心。医院日门诊量…

Linux (Ubuntu)- mysql8 部署

1.基本部署 01》》先查看OS类型&#xff0c;如果是Ubuntu在往下边看 rootspray:/etc/mysql/mysql.conf.d# lsb_release -a LSB Version: core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: …

Movavi Video Converter 2022 for Mac/Win:卓越的视频音频文件转换器

在数字化时代&#xff0c;视频和音频文件已成为我们日常生活和工作中不可或缺的一部分。无论是制作精美的家庭影片&#xff0c;还是编辑专业的商业视频&#xff0c;一款高效、便捷的视频音频文件转换器无疑是您的得力助手。而Movavi Video Converter 2022&#xff0c;就是这样一…

GPT 模型解析:ChatGPT 如何在语言处理领域引领潮流?

人工智能时代来临 我们正处于AI的iPhone时刻。——黄仁勋&#xff08;英伟达CEO&#xff09; ChatGPT 好得有点可怕了&#xff0c;我们距离危险的强人工智能不远了。——马斯克&#xff08;Tesla/SpaceX/Twitter CEO&#xff09; 以上的内容说明我们现在正处于一个技术大翻牌的…

测斜仪在边坡安全监测中的重要作用

边坡作为土木工程和地质工程领域中常见的结构形式&#xff0c;其稳定性直接关系到工程安全以及人民生命财产的安全。因此&#xff0c;对边坡进行精确、及时的监测是至关重要的。在众多边坡监测仪器中&#xff0c;测斜仪以其独特的优势在边坡安全监测中发挥着重要的作用。 测斜仪…

(24年4月2日更新)Linux安装chrome及chromedriver(Ubuntu20.0416.04)

一、安装Chrome 1&#xff09;先执行命令下载chrome&#xff1a; wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb2&#xff09;安装chrome sudo dpkg -i google-chrome-stable_current_amd64.deb踩坑&#xff1a;这里会提示如下报错&…

C刊级 | Matlab实现GWO-BiTCN-BiGRU-Attention灰狼算法优化双向时间卷积双向门控循环单元融合注意力机制多变量回归预测

C刊级 | Matlab实现GWO-BiTCN-BiGRU-Attention灰狼算法优化双向时间卷积双向门控循环单元融合注意力机制多变量回归预测 目录 C刊级 | Matlab实现GWO-BiTCN-BiGRU-Attention灰狼算法优化双向时间卷积双向门控循环单元融合注意力机制多变量回归预测效果一览基本介绍程序设计参考…

大数据实验一,Hadoop安装及使用

目录 一&#xff0e;实验内容 二&#xff0e;实验目的 三&#xff0e;实验过程截图及说明 1、安装SSH&#xff0c;并配置SSH无密码登录 2、配置java环境 3.Hadoop的安装与配置 4、修改四个配置文件&#xff1a; 5、格式化HDFS的NameNode&#xff1a; 6、启动Hadoop 7、…

springcloud基本使用五(Gateway服务网关)

为什么使用网关&#xff1f; 权限控制&#xff1a;网关作为微服务入口&#xff0c;需要校验用户是是否有请求资格&#xff0c;如果没有则进行拦截。 路由和负载均衡&#xff1a;一切请求都必须先经过gateway&#xff0c;但网关不处理业务&#xff0c;而是根据某种规则&#xff…

[NSSRound#8 Basic]MyPage

[NSSRound#8 Basic]MyPage 打开页面后什么都没有 尝试使用php伪协议 //读取文件源码 filephp://filter/readconvert.base64-encode/resourceindex.php 显示&#xff1a;空白一片 filephp://filter/readconvert.base64-encode/resource/var/www/html/index.php 显示&#xff1…

C#.手术麻醉系统源码 手麻系统如何与医院信息系统进行集成?

C#.手术麻醉系统源码 手麻系统如何与医院信息系统进行集成&#xff1f; 手术麻醉系统与医院信息系统的集成是一个关键步骤&#xff0c;它有助于实现信息的共享和流程的协同&#xff0c;从而提高医疗服务的效率和质量。手麻系统与lis、his、pacs等系统的对接是医院信息化建设的重…

00150金融理论与实务考试分析

1.考试时间及题型 2.历年真题分析—单选题 3.历年真题分析—多选题

Redis windows设置自动开启服务

查看服务 WinR后输入services.msc进入到服务管理页面&#xff0c;查看是否存在Redis服务。 Windows版Redis解压后&#xff0c;是不会在服务中显示的&#xff0c;需要手动配置后才能在服务中看到 配置服务 在解压的Redis版本目录下&#xff0c;输入CMD&#xff0c;运行命…

使用阿里云试用Elasticsearch学习:1.1 基础入门——入门实践

阿里云试用一个月&#xff1a;https://help.aliyun.com/search/?kelastic&sceneall&page1 官网试用十五天&#xff1a;https://www.elastic.co/cn/cloud/cloud-trial-overview Elasticsearch中文文档&#xff1a;https://www.elastic.co/guide/cn/elasticsearch/guide…

Python PDF页面设置 -- 旋转页面、调整页面顺序

在将纸质文档扫描成PDF电子文档时&#xff0c;有时可能会出现页面方向翻转或者页面顺序混乱的情况。为了确保更好地浏览和查看PDF文件&#xff0c;本文将分享一个使用Python来旋转PDF页面或者调整PDF页面顺序的解决方案。 目录 使用Python旋转PDF页面 使用Python调整PDF页面…

RISC-V GNU Toolchain 工具链安装问题解决(含 stdio.h 问题解决)

我的安装过程主要参照 riscv-collab/riscv-gnu-toolchain 的官方 Readme 和这位佬的博客&#xff1a;RSIC-V工具链介绍及其安装教程 - 风正豪 &#xff08;大佬的博客写的非常详细&#xff0c;唯一不足就是 sudo make linux -jxx 是全部小写。&#xff09; 工具链前前后后我装了…