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

走进computed,了解computed的前世今生

computed(计算属性)并不是vue独创的,而是源自计算机科学和响应式编程的长期发展

计算理论的奠基:

  • 函数式编程的纯函数思想:计算属性的核心特征(无副作用、依赖输入确定输出)直接来源于函数式编程中的纯函数概念
  • 响应式编程的衍生值:在响应式系统中,任何可以依据其他的“可观察量”自动计算出的值都可以视为计算属性

vue中computed的演进

vue 1.x时代:(2014-2016)
初步实现:通过 computed 选项定义计算属性,基于依赖收集的响应式系统
特点:

  • 基于Object.defineProperty实现响应式
  • 计算属性会被混入到Vue实例中,像普通属性一样访问
  • 已具备缓存机制,依赖不变时直接返回缓存值

Vue 2.x 时代:(2016-2020)
优化改进:

  • 计算属性的惰性求值:只在真正被访问时才计算
  • 更精细的依赖追踪:基于 Watcher 类的依赖收集系统
  • 支持 setter:允许通过赋值反向影响依赖项3
computed: {fullName: {get() { return this.firstName + ' ' + this.lastName },set(newValue) { /* 处理赋值逻辑 */ }}
}

Vue 3.x 时代(2020至今)
组合式 API 重构:

  • 引入 computed() 函数,可在 setup() 或 在 < script setup > 中使用
  • 基于 Proxy 的响应式系统,解决 Vue 2 的检测限制
  • 与 Reactivity System 深度集成,性能显著提升
import { computed } from 'vue'
const count = ref(1)
const double = computed(() => count.value * 2)

computed设计核心原理:

1、响应式依赖追踪

  • 自动收集依赖:执行计算属性时候,访问的每个属性都会被记录依赖
  • 更新触发:当任何属性变化时候,标记计算属性为“脏”dirty, 下次访问时候再重新计算

2、缓存机制的实现

// 简化的 computed 实现原理
function computed(getter) {let valuelet dirty = trueconst runner = effect(getter, {lazy: true,scheduler: () => {dirty = true // 依赖变化时标记为需要重新计算trigger(obj, 'value') // 触发依赖此计算属性的副作用}})const obj = {get value() {if (dirty) {value = runner() // 执行 getter 计算新值dirty = false}return value}}return obj
}

在vue中 同步派生状态指的是基于现有响应式数据立即计算得出的新状态,这种计算是同步完成的,具有即时性和准确性,这是computed计算属性的核心设计理念

同步派生状态的核心特征
1、即时计算:当依赖变化时候,派生值同步(立即)重新计算

const count = ref(1);
const double = computed(() => count.value * 2); // 同步计算
console.log(double.value); // 2
count.value = 3; 
console.log(double.value); // 6 (立即更新)

2、纯函数特性:派生过程不允许修改外部状态,仅依赖输入:

// 纯派生:结果仅取决于 count.value
const triple = computed(() => count.value * 3); 

3、与异步派生对比

特性同步派生 (computed)异步派生 (如 watch + 状态)
执行时机立即完成延迟完成(需等待 Promise)
模板引用可直接使用需要处理加载中/错误状态
响应式追踪自动追踪依赖需手动管理依赖
返回值必须返回无返回值(通过修改状态输出)

需要每次执行的逻辑

1、模板渲染的即时性需求:vue模板渲染需要同步获取值进行渲染,异步派生会导致渲染中断或需要额外的处理加载状态
2、响应式系统的设计基础:vue的响应式依赖追踪依赖同步访问
3、缓存优化前提:同步计算使得缓存机制能精准判断是否需要重新计算

同步派生使用场景:

// 数据格式化
const price = ref(100);
const formattedPrice = computed(() => `¥${price.value.toFixed(2)}`);// 过滤/筛选列表
const todos = ref([/*...*/]);
const activeTodos = computed(() => todos.value.filter(todo => !todo.completed)
);// 多状态组合
const width = ref(10);
const height = ref(20);
const area = computed(() => width.value * height.value);

同步派生的优势:

  • 性能高效:基于依赖追踪的精准缓存
  • 心智模型简单:输入输出关系明确
  • 调试友好:无隐藏的异步时序问题
  • 组合方便:可安全地被其他计算属性引用

vue中的“同步派生状态”是通过computed来实现的即时性、确定性的数据转换,它是vue响应式系统的核心支柱,这种设计确保了:

  • 模版渲染的即时性、可靠性
  • 状态变化的可预测性
  • 派生逻辑的高效性

computed为什么会有缓存机制:

vue中的computed采用缓存机制主要是为了优化性能和保证一致性:当计算属性依赖性没有变化时,vue会直接返回上一次的计算结果,而不会重新计算

缓存优势:

1、性能优化:

  • 避免重复计算:模版中多次引用同一个计算属性时,依赖未变化就不需要重新计算;如果没有缓存,expensiveCalc 则会在每次渲染时候都需要计算
<template><div>{{ expensiveCalc }}</div> <!-- 计算一次 --><div>{{ expensiveCalc }}</div> <!-- 直接读缓存 -->
</template>
  • 减少渲染开销: vue的渲染更新是批量的,缓存机制可以避免同一轮更新中多次触发相同的计算

2、保证一致性

  • 同步派生状态的确定性:在同一个事件循环中,无论访问多少次计算属性,其结果都是相同
  • 避免副作用污染:计算属性时纯函数,缓存机制强调开发者遵守这一原则, 无法在计算属性中执行副作用

3、与响应式系统的协同

  • 精准依赖追踪:缓存机制依赖vue的响应式系统,只有当确定的依赖项变化时候才需要重新计算

缓存实现的关键技术

1、惰性计算:只有在真正访问属性时才会计算,如果依赖性没有变化则直接读取缓存值
2、标记-清除机制:“脏”(dirty)状态标记:当依赖性发生变化时,标记属性为“dirty”. 下次访问时候再重新计算

// 伪代码示意Vue内部逻辑
class ComputedRef {constructor(getter) {this._dirty = true; // 初始标记为需要计算this._value = undefined;}get value() {if (this._dirty) {this._value = getter(); // 重新计算this._dirty = false;}return this._value;}
}

3、依赖收集的精准性

  • 计算属性执行时会自动追踪所有被访问的响应式变量
  • 只有这些被追踪的变量变化时才会触发重新计算

何时会打破缓存:

  • 依赖项发生变化(主动触发):
const a = ref(1);
const b = computed(() => a.value * 2);
a.value = 2; // 修改依赖,下次访问b时重新计算
  • 手动强制刷新(特殊情况)
// 部分场景可通过赋值触发(不推荐)
b.value = 5; // 如果计算属性可写(setter)

与方法的对比:

特性computed (带缓存)方法 (无缓存)
执行时机依赖变化时每次调用都执行
性能高效(缓存结果)可能重复计算
模板使用自动优化需自行优化(如 v-once)
适用场景纯计算、派生状态需要每次执行的逻辑
http://www.xdnf.cn/news/1219879.html

相关文章:

  • 【未解决】STM32无刷电机驱动电路问题记录
  • 数据库学习--------数据库日志类型及其与事务特性的关系
  • RPA-重塑企业自动化流程的智能引擎
  • 8.2-使用字符串存储 UTF-8 编码文本
  • 推客小程序商业模型设计:合规分佣体系×盈利模式×LTV提升策略
  • 法式基因音响品牌SK(SINGKING AUDIO)如何以硬核科技重塑专业音频版图
  • Linux731 shell工具;[]字符
  • 【源力觉醒 创作者计划】文心一言与deepseek集成springboot开发哪个更方便
  • ASP.NET Core中使用NLog和注解实现日志记录
  • 某讯视频风控参数逆向分析
  • 计算机存储正数,负数
  • .NET Core部署服务器
  • 搭建 Mock 服务,实现前端自调
  • Rust × WebAssembly 项目脚手架详解
  • 正向运动学(Forward Kinematics,简称FK)和逆向运动学(Inverse Kinematics,简称IK)
  • ABS系统专用磁阻式汽车轮速传感器
  • 【扩散模型专栏】01 扩散模型入门:概念与背景
  • USRP捕获手机/路由器数据传输信号波形(中)
  • 多云场景实战:华为手机 QR 码绑定与 AWS云服务器终端登录全解
  • 【n8n教程笔记——工作流Workflow】文本课程(第二阶段)——1 理解数据结构 (Understanding the data structure)
  • Day15--二叉树--222. 完全二叉树的节点个数,110. 平衡二叉树,257. 二叉树的所有路径,404. 左叶子之和
  • 基于 Amazon Nova Sonic 和 MCP 构建语音交互 Agent
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(12):文法+单词
  • O2OA 平台:助力企业在信创浪潮下实现高效国产化转型
  • Python单例类、元类详解
  • FFmpegHandler 功能解析,C语言程序化设计与C++面向对象设计的核心差异
  • 【科普】在STM32中有哪些定时器?
  • 掩码语言模型(MLM)技术解析:理论基础、演进脉络与应用创新
  • Spring AI 系列之二十八 - Spring AI Alibaba-基于Nacos的prompt模版
  • Java实习面试记录