作为一名大三正在啃 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 绑定),就要用 get 和 set 完整形式。
举个拆分姓名的例子:输入[张三 李四],自动拆分成 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 的计算属性看似简单,实则藏了不少细节,尤其是缓存和传参这两个点,踩过坑才知道有多重要。