Vue3 计算属性|从基础缓存到可读写

作为一名大三正在啃 Vue3 的学生,今天刚啃完计算属性(computed)的核心知识点,踩了几个小坑也理清了关键区别,赶紧整理成笔记,既方便自己复习,也希望能帮到同样在学 Vue 的小伙伴~

一、为什么需要计算属性?

先抛个场景:如果我们需要在模板里展示「任务完成数量」,直接写{{list.filter((item) =>item.done).length}}虽然能实现,但模板里塞太多逻辑会变得臃肿、难维护、性能低;如果用普通函数调用,又会有重复计算的问题。

而计算属性就是为了解决这个问题而生的 ------ 它基于依赖缓存,只在依赖变化时重新计算,既简洁又高效。

1. 看看基础的使用

XML 复制代码
<script setup lang="ts">
import { ref, computed } from 'vue'

// 模拟任务列表完成情况
const list = ref([{done:true},{done:false},{done:true}])

// 计算属性:统计完成的任务数
const donecount = computed(()=>{
  console.log('donecount(计算属性)执行了')
  return list.value.filter((item) => item.done).length
})

// 普通函数:同样统计完成的任务数
function donecount2 () {
  console.log('donecount2(普通函数)执行了')
  return list.value.filter((item) => item.done).length
}
</script>

<template>
  <!-- 直接写模板表达式(不推荐,逻辑冗余) -->
  任务完成数量:{{ list.filter((item) => item.done).length }}
  <br>
  
  <!-- 计算属性调用(无括号) -->
  计算属性版:{{ donecount }} - {{ donecount }}
  <br>
  
  <!-- 普通函数调用(必须加括号) -->
  普通函数版:{{ donecount2() }} - {{ donecount2() }}
</template>

当我们运行,打开界面,去控制台查看时会发现只输出了一遍"donecount(计算属性)执行了",而"donecount2(普通函数)执行了"却执行了两遍,为什么呢?


二、核心优势:缓存机制(计算属性 vs 普通函数)

这是计算属性最关键的特性,也是和普通函数最大的区别!

1. 首次渲染的差异

运行上面的代码,控制台打印结果:

donecount(计算属性)执行了

donecount2(普通函数)执行了

donecount2(普通函数)执行了

  • 计算属性donecount虽然在代码中写了两次,实际只执行了一次。
  • 普通函数donecount()写了两次,实际上也调用了两次------因为函数每次调用都会重新。

2. 依赖变化时的差异

如果我们加上一个修改按钮

XML 复制代码
<script setup lang="ts">
// 新增:修改依赖的函数
function modify () {
  list.value[0].done = false
}
</script>

<template>
  <button @click="modify">修改第一个任务状态</button>
</template>

点击按钮后,list的依赖发生变化:

  • 计算属性 donecount会重新执行 1 次(因为依赖的 list 变了);
  • 普通函数 donecount2() 只有再次调用时才会执行,但每次调用依然是重新计算。

3. 结论

  • 计算属性:基于依赖缓存,依赖不变时,多次访问只返回缓存结果,不重复计算;
  • 普通函数:无缓存,每次调用都会重新执行函数体。

三、进阶:计算属性如何传参?

计算属性本身是无参 的,直接写**computed((index) => {})** 会报错!那如果我们想根据索引取菜单名称(比如**menu(0)**取 首页),该怎么做?

答案:让计算属性返回一个函数,通过函数传参实现!

1. 示例

XML 复制代码
<script setup lang="ts">
import { reactive, computed } from 'vue'

// 模拟菜单列表
const menuList = reactive([
  {name:'首页'},
  {name:'新闻'}
]) 

// 计算属性返回函数,实现传参
const menu = computed(() => {
  // 这里的index就是我们要传的参数
  return (index:number)=>{
    return menuList[index]?.name 
  }
})
</script>

<template>
  菜单1:{{ menu(0) }} <!-- 输出:首页 -->
  菜单2:{{ menu(1) }} <!-- 输出:新闻 -->
</template>

这个写法既保留了计算属性对 menuList依赖监听 ( menuList变了会重新计算),又实现了参数传递


四、高级用法:可读写的计算属性(完整形式)

默认的计算属性是只读 的(只有 getter),如果我们需要给计算属性赋值(比如通过 v-model 绑定),就要用 getset 完整形式。

举个拆分姓名的例子:输入[张三 李四],自动拆分成 firstName 和 lastName;修改 firstName/lastName,计算属性也会同步更新

1. 案例

XML 复制代码
<script setup lang="ts">
import { ref, computed } from 'vue'

const firstName = ref('')
const lastName = ref('')

// 可读写的计算属性
const msg2 = computed({
  // 读取值时触发(getter)
  get(){
    return firstName.value + lastName.value
  },
  // 赋值时触发(setter)
  set(value:string){
    // 把输入的字符串按空格拆分成姓和名
    const [n1,n2] = value.split(' ')
    firstName.value = n1 || ''
    lastName.value = n2 || ''
    console.log('赋值的内容:', value);
  }
})
</script>

<template>
  输入姓名:<input v-model="msg2"> 
  完整名字: {{ msg2 }}
  <!-- 测试:输入「张三 李四」,firstName会变成「张三」,lastName变成「李四」 -->
</template>

注意:普通的计算属性(只写 getter)是只读的,如果直接赋值会报错,比如:

XML 复制代码
const msg1 = computed(()=>{
  return 'xxx'
})
// 报错:Set operation on key "msg1" failed: target is readonly.

五、总结:计算属性的使用场景 & 注意事项

1. 什么时候用计算属性?

  • 需要基于现有数据做 派生计算(比如统计、拼接、过滤);
  • 希望避免重复计算,提升性能(缓存特性);

2. 核心注意点

  • 计算属性必须有返回值;
  • 依赖必须是响应式数据(ref/reactive),否则不会触发重新计算;
  • 只读计算属性用单函数形式,可读写用 get/set 形式;
  • 传参通过返回函数实现,不要直接给 computed 传参;

今天的笔记就到这啦~Vue 的计算属性看似简单,实则藏了不少细节,尤其是缓存和传参这两个点,踩过坑才知道有多重要。

相关推荐
ZPC821013 小时前
如何创建一个单例类 (Singleton)
开发语言·前端·人工智能
紫_龙14 小时前
最新版vue3+TypeScript开发入门到实战教程之重要详解readonly/shallowReadOnly
前端·javascript·typescript
roamingcode15 小时前
前端 AI Agent 多智能体协作架构:从对抗式排查到工作流解耦
前端·人工智能·架构·agent·team
蓝莓味的口香糖16 小时前
【vue】初始化 Vue 项目
前端·javascript·vue.js
aikongmeng16 小时前
【Ai】Claude Code 初始化引导
javascript
光影少年17 小时前
数组去重方法
开发语言·前端·javascript
我命由我1234517 小时前
浏览器的 JS 模块化支持观察记录
开发语言·前端·javascript·css·html·ecmascript·html5
weixin_4434785117 小时前
Flutter第三方常用组件包之路由管理
前端·javascript·flutter
武藤一雄17 小时前
C# 异步回调与等待机制
前端·microsoft·设计模式·微软·c#·.netcore