资讯动态

Vue 3 深入学习笔记 01:computed 计算属性与缓存机制

发布时间:2026/10/20 11:16:51 来源:尧图企业网站定制
Vue 3 深入学习笔记 01响应式系统与 computed 缓存机制一、computed 的核心作用computed用于根据已有的响应式数据计算出新的派生数据并对计算结果进行缓存。例如订单总价由商品单价和购买数量决定script setup import { ref, computed } from vue const price ref(100) const count ref(2) const total computed(() { console.log(计算总价) return price.value * count.value }) /script这里price商品单价基础状态。count购买数量基础状态。total订单总价派生状态。total不需要单独维护只需要根据price和count自动计算即可。核心思想能够从已有状态推导出来的数据通常不需要再单独保存一份。这样可以避免多个状态之间需要手动同步的问题。二、computed 与普通函数的区别1. 普通函数constpriceref(100)constcountref(2)constgetTotal(){console.log(计算总价)returnprice.value*count.value}console.log(getTotal())console.log(getTotal())输出计算总价 200 计算总价 200普通函数每次调用都会重新执行函数体。2. computedconstpriceref(100)constcountref(2)consttotalcomputed((){console.log(计算总价)returnprice.value*count.value})console.log(total.value)console.log(total.value)输出计算总价 200 200第二次访问时没有再次执行计算函数而是复用了之前的计算结果。3. 对比总结对比项普通函数computed调用时是否执行函数体每次调用都会执行缓存失效或首次读取时才执行是否缓存计算结果否是依赖数据发生变化下次调用重新计算缓存失效下次读取时重新计算适用场景普通计算、业务操作响应式派生数据注意computed缓存的是计算结果不代表整个组件或页面都被缓存。三、computed 为什么能够缓存1. Vue 会追踪响应式依赖constpriceref(100)constcountref(2)consttotalcomputed((){returnprice.value*count.value})执行计算函数时Vue 会追踪其中读取的响应式数据。依赖关系可以表示为price.value ──┐ ├── total count.value ──┘total依赖price.value和count.value。当这些依赖发生变化时Vue 能够知道total的缓存可能已经过期。2. 首次读取时发生了什么console.log(total.value)执行过程读取 total.value | v 检查计算结果是否有效 | v 首次读取没有可用缓存 | v 执行计算函数 | v 读取 price.value 和 count.value | v 追踪相关响应式依赖 | v 计算结果 200 并缓存 | v 返回 2003. 再次读取时发生了什么console.log(total.value)如果依赖没有变化Vue 可以直接返回缓存的结果。因此不需要再次执行计算函数。4. 依赖变化时发生了什么count.value3console.log(total.value)执行过程修改 count.value | v Vue 检测到响应式数据变化 | v 通知依赖该数据的计算属性 | v 计算属性的缓存失效 | v 下次读取 total.value | v 重新执行计算函数 | v 返回最新结果 300这里最重要的一点依赖发生变化不等于计算函数立即执行。对于这里讨论的惰性计算属性依赖变化后Vue 会使计算结果失效通常在下次读取时重新计算。这种机制称为惰性求值Lazy Evaluation。四、Vue 如何知道 computed 依赖哪些数据Vue 的依赖追踪不是简单地扫描代码中的变量名而是在代码执行过程中追踪实际发生的响应式数据读取。例如constpriceref(100)constcountref(2)constdiscountref(0.8)consttotalcomputed((){if(count.value0){returnprice.value*count.value*discount.value}return0})当count.value 0时计算函数会读取count.valueprice.valuediscount.value这三个响应式数据都会成为本次计算的依赖。当count.value为0时函数直接返回0不会读取price.value和discount.value。因此在这次执行中后两个数据不会作为该次计算的依赖。如果之后count从0变为2计算函数重新执行并读取price和discount依赖关系也会随之调整。核心结论响应式依赖可以随着计算函数的实际执行路径动态变化。五、computed 与后端开发的联系从后端开发的角度可以将computed理解为一种能够感知响应式依赖的缓存计算机制。例如constunitPriceref(100)constquantityref(3)consttotalPricecomputed((){returnunitPrice.value*quantity.value})这里unitPrice和quantity是基础状态。totalPrice是根据基础状态推导出来的数据。基础状态变化后派生数据可以自动得到更新。不推荐将所有数据都独立保存constunitPriceref(100)constquantityref(3)consttotalPriceref(300)因为每次修改单价或数量时都必须手动更新总价。只要有一处遗漏就可能造成数据不一致。更合理的方式是通过computed派生总价。但需要注意computed并不是通用的后端缓存系统它主要服务于 Vue 应用中的响应式派生状态不负责网络请求缓存、持久化或跨用户共享。六、computed、watch 与普通函数的职责1. computed计算派生数据适用场景商品总价。表单是否满足提交条件。筛选后的列表。根据多个状态生成的展示文本。constpriceref(100)constcountref(2)consttotalcomputed(()price.value*count.value)2. watch响应数据变化执行副作用适用场景搜索条件变化后请求后端。某个状态变化后同步外部系统。监听特定数据并执行相应操作。watch(count,async(newCount){console.log(数量发生变化,newCount)})需要根据业务决定是否立即执行、是否需要取消旧请求等。3. 普通函数执行明确的业务操作适用场景提交订单。删除记录。点击按钮执行操作。进行一次性的普通计算。constsubmitOrderasync(){// 调用后端接口提交订单}4. 对比总结需求推荐方式根据响应式数据计算结果computed监听数据变化并执行副作用watch执行明确的业务操作普通函数或事件处理函数不要在computed中执行网络请求、修改其他状态等副作用操作。七、computed 的只读特性默认情况下通过 getter 创建的计算属性是只读的。consttotalcomputed(()price.value*count.value)下面的操作不符合只读计算属性的使用方式total.value500如果业务确实需要双向读写可以使用带有get和set的计算属性constpriceref(100)constcountref(2)consttotalcomputed({get(){returnprice.value*count.value},set(newTotal){count.valuenewTotal/price.value}})此时给total.value赋值会调用set由我们定义如何更新基础状态。注意上面的示例没有处理price为0等特殊情况。实际业务中需要根据业务规则进行校验。八、手动练习预测运行结果题目import{ref,computed}fromvueconstaref(10)constbref(20)constresultcomputed((){console.log(执行计算)returna.valueb.value})console.log(result.value)a.value30console.log(修改完成)console.log(result.value)console.log(result.value)b.value50console.log(result.value)问题问题一执行计算 一共打印几次问题二每次打印 result.value 时的返回值是什么问题三为什么修改 a 和 b 不会直接触发计算函数执行参考答案1. 完整输出执行计算 30 修改完成 执行计算 50 50 执行计算 802. 三个问题的答案问题一执行计算一共打印几次答案3 次。分别对应第一次读取result.value。a变化后第一次重新读取。b变化后第一次重新读取。问题二每次打印result.value时的返回值是什么按代码中的读取顺序读取位置返回值第一次读取30修改a后第一次读取50紧接着第二次读取50修改b后读取80问题三为什么修改a和b不会直接触发计算函数执行因为computed采用惰性求值机制。依赖变化时Vue 会让计算结果失效在下次访问result.value时才重新执行计算函数。2. 执行过程分析第一次读取console.log(result.value)此时result没有可用的缓存因此执行计算函数执行计算 30修改 aa.value30console.log(修改完成)a发生变化result的缓存失效。但由于还没有再次读取result.value计算函数不会立即执行。因此输出修改完成第二次读取console.log(result.value)缓存已经失效因此重新计算执行计算 50第三次读取console.log(result.value)由于a和b没有再次变化缓存有效直接返回5050不会再次打印执行计算。修改 b 并读取结果b.value50console.log(result.value)b发生变化缓存失效。随后读取result.value重新计算执行计算 804. 补充一个重要细节以上结论适用于本例中的正常、同步、非调试模式的计算属性使用场景。在 Vue 3 中computed的内部实现还涉及依赖版本、脏状态判断以及响应式系统的调度机制。实际实现比简单的“缓存有效或无效”模型更精细但这个模型足以帮助我们理解当前示例。九、本节相关面试题1. computed 有什么作用答案computed用于根据已有的响应式数据计算出新的派生数据并对计算结果进行缓存。例如商品总价可以通过单价和数量计算得到不需要单独维护一个总价状态。constpriceref(100)constcountref(2)consttotalcomputed(()price.value*count.value)2. computed 和普通函数有什么区别答案主要区别是缓存机制。普通函数每次调用都会重新执行。computed缓存计算结果依赖没有变化时重复读取可以复用缓存。因此computed适合计算响应式派生数据普通函数适合执行普通逻辑或业务操作。3. computed 为什么能够缓存答案因为 Vue 会追踪计算属性 getter 中读取的响应式数据。当依赖没有变化时computed可以复用之前的计算结果当依赖发生变化时缓存可能失效需要时再重新计算。4. computed 的依赖发生变化后会立即重新计算吗答案通常不会。computed采用惰性求值机制。依赖变化后计算结果会失效但一般要等到下次读取计算属性时才会重新计算。5. Vue 是如何追踪 computed 的依赖的答案Vue 会在计算函数执行过程中追踪实际读取的响应式数据。例如constresultcomputed((){returna.valueb.value})这里result依赖a和b。当它们发生变化时Vue 就能知道计算结果可能已经过期。6. computed 的依赖是固定的吗答案不是依赖关系会根据计算函数的实际执行路径动态变化。constresultcomputed((){returnenabled.value?a.value:b.value})当enabled为true时本次计算依赖enabled和a为false时则依赖enabled和b。7. computed、watch 和普通函数有什么区别答案computed计算派生数据例如商品总价。watch监听数据变化并执行副作用例如发送网络请求。普通函数执行明确的业务操作例如提交订单。记忆口诀computed 负责计算watch 负责监听普通函数负责执行操作。8. computed 默认是只读的吗如何实现可写答案是的通过 getter 创建的computed默认是只读的。如果需要修改计算属性可以同时定义get和setconsttotalcomputed({get(){returnprice.value*count.value},set(newTotal){count.valuenewTotal/price.value}})读取时调用get赋值时调用set具体如何修改基础状态由开发者决定。9. 为什么不推荐在 computed 中发送网络请求或修改其他状态答案因为computed主要用于计算派生数据应该尽量保持纯粹不执行副作用操作。网络请求、数据同步等操作更适合使用watch或watchEffect。10. computed 的缓存和组件缓存、后端缓存有什么区别答案computed缓存的是计算属性的计算结果不是整个组件或页面。它也不是通用的后端缓存不负责网络请求缓存、数据持久化或跨用户共享。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑