🌟 深入理解 Vue 3 计算属性与侦听器:提升性能与响应式的利器
在 Vue 3 的响应式系统中,computed(计算属性)和 watch(侦听器)是两个非常重要的工具。它们帮助我们更优雅地处理数据依赖和副作用,但用途和机制截然不同。
本文将带你深入理解:
- 什么是计算属性?它和方法有什么本质区别?
watch的函数写法 vs 对象写法,哪种更适合团队开发?- 实际项目中如何高效使用
watch,并避免常见陷阱。
📌 一、计算属性(Computed):基于依赖的“自动缓存”值
✅ 什么是计算属性?
computed 用于声明一个依赖其他响应式数据的值,它会自动监听依赖的变化,并在需要时重新计算结果。
🔍 核心特点:缓存 + 响应式 + 惰性求值
示例:计算全名
<template>
<div>
姓:<input v-model="firstName" />
名:<input v-model="lastName" />
<p>全名:{{ fullName }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 计算属性
const fullName = computed(() => {
return firstName.value + lastName.value
})
</script>
当你修改 firstName 或 lastName 时,fullName 会自动更新。
🔁 计算属性 vs 方法:关键区别在哪?
你可能会问:我也可以用一个方法来实现同样的功能啊?
const getFullName = () => {
return firstName.value + lastName.value
}
表面上看效果一样,但底层机制完全不同!
| 对比项 | 计算属性 computed | 方法 methods |
|---|---|---|
| 是否有缓存? | ✅ 有缓存,依赖不变时不重新执行 | ❌ 无缓存,每次渲染都调用 |
| 性能表现 | 高效,适合复杂计算 | 低效,重复执行浪费性能 |
| 适用场景 | 依赖响应式数据的派生值 | 每次都需要重新执行的逻辑 |
🧪 举个例子说明区别:
const fullNameComputed = computed(() => {
console.log('✅ 计算属性执行了')
return firstName.value + lastName.value
})
const getFullNameMethod = () => {
console.log('❌ 方法执行了')
return firstName.value + lastName.value
}
<p>计算属性:{{ fullNameComputed }}</p>
<p>方法调用:{{ getFullNameMethod() }}</p>
当你触发其他无关更新(比如弹窗状态变化)时:
计算属性:只在firstName或lastName改变时打印日志方法:每次组件重新渲染都会打印日志!
💡 结论:对于依赖响应式数据的“派生值”,优先使用
computed,避免性能浪费。
🎯 何时使用计算属性?
✅ 推荐使用 computed 的场景:
- 拼接字符串(如全名)
- 过滤数组(如已完成任务列表)
- 计算总价、平均值等
- 条件判断逻辑(如
isUserValid)
示例:过滤任务列表
const tasks = ref([
{ id: 1, title: '学习 Vue', done: true },
{ id: 2, title: '写代码', done: false },
{ id: 3, title: '休息', done: true }
])
// 只显示已完成的任务
const doneTasks = computed(() => {
return tasks.value.filter(task => task.done)
})
<ul>
<li v-for="task in doneTasks" :key="task.id">{{ task.title }}</li>
</ul>
这个过滤操作只有在 tasks 变化时才会重新执行,而不是每次渲染都过滤一遍。
🔍 侦听器(Watch):监听变化并执行副作用
✅ 什么是侦听器?
watch 用于监听某个响应式数据的变化,并在其改变时执行副作用操作(如发送请求、保存本地存储、打印日志等)。
⚠️ 注意:
watch不用于生成值,而是用于“响应变化”做事情。
🛠 watch 的两种写法:函数式 vs 对象式
1. 函数写法(简洁但功能有限)
watch(keyword, (newVal, oldVal) => {
console.log(`关键词从 "${oldVal}" 变为 "${newVal}"`)
})
✅ 优点:写法简单
❌ 缺点:无法配置 deep、immediate 等选项
2. 对象写法(推荐!功能完整)
watch(keyword, {
handler(newVal, oldVal) {
console.log(`关键词从 "${oldVal}" 变为 "${newVal}"`)
},
immediate: true, // 立即执行一次
deep: true // 深度监听(对对象有效)
})
✅ 优点:
- 可以同时设置
immediate和deep - 逻辑更清晰,适合复杂场景
- 团队协作中更易维护
💡 实际开发建议:只记对象写法就够了!
很多开发者在项目中来回切换写法,容易出错。建议:统一使用对象写法,一劳永逸。
✅ 推荐模板(可复制粘贴):
watch(dataSource, {
handler: async (newVal, oldVal) => {
// 执行副作用:发请求、存 localStorage、更新状态等
if (newVal !== oldVal) {
await fetchData(newVal)
}
},
immediate: true, // 页面加载时立即执行一次
deep: true // 如果监听的是对象或数组,确保开启 deep
})
📌 即使你不需要
immediate或deep,也可以显式写上false,提高代码可读性:
watch(form, {
handler: (newVal) => {
console.log('表单变化了')
},
immediate: false,
deep: true // 表单是对象,建议开启 deep
})
🎯 何时使用 deep: true?
- 监听对象内部属性变化时必须开启
- 监听数组元素变化(如
push、splice)时通常不需要deep,因为 Vue 能检测到数组 mutation 方法
const user = ref({ name: 'Alice', profile: { age: 20 } })
watch(user, {
handler: (newVal) => {
console.log('用户信息变了')
},
deep: true // 必须开启,否则修改 profile.age 不会触发
})
🎯 何时使用 immediate: true?
- 需要在页面加载时立刻执行一次回调
- 常用于:根据初始值发起请求、初始化状态
watch(routeQuery, {
handler: (query) => {
fetchList(query)
},
immediate: true // 页面打开就拉数据
})
🆚 computed vs watch:核心区别总结
| 特性 | computed | watch |
|---|---|---|
| 用途 | 生成一个新的响应式值 | 执行副作用(如 API 请求、日志) |
| 返回值 | 必须返回一个值 | 不返回值,用于“做事” |
| 缓存机制 | ✅ 有缓存,依赖不变不重新计算 | ❌ 无缓存,每次变化都执行回调 |
| 性能 | 高(惰性求值) | 中(可控制执行频率) |
| 是否响应式 | 是,返回值是响应式的 | 否,回调函数本身不是响应式值 |
✅ 简单判断标准:
- 想要一个值(用于模板显示)? → 用
computed - 想要“做件事”(如发请求、存 localStorage)? → 用
watch
✅ 最佳实践建议
| 场景 | 推荐方案 |
|---|---|
| 拼接字符串、过滤列表、计算数值 | ✅ computed |
| 发送网络请求、保存数据到本地 | ✅ watch(对象写法) |
| 频繁调用且依赖响应式数据的方法 | ✅ 改为 computed |
| 监听路由、表单、搜索关键词 | ✅ watch + immediate: true |
| 监听对象深层变化 | ✅ watch + deep: true |
| 避免在模板中调用方法进行计算 | ❌ 应使用 computed 替代 |
🎯 结语
在 Vue 3 开发中,合理使用 computed 和 watch 能让你的代码更清晰、更高效。
💡 记住三句话:
computed是“我要一个值”watch是“我要做件事”watch只记对象写法,deep和immediate直接写上,省心又安全
尤其是 computed 与 methods 的选择,直接影响应用性能。避免在模板中滥用方法进行计算,善用 computed 的缓存机制,是写出高性能 Vue 应用的关键。
现在,就去检查你的代码,把那些本该是 computed 的方法重构掉,并统一 watch 的写法吧!
📌 延伸阅读:
- Vue 3 官方文档 - 计算属性
watchEffect的使用(自动追踪依赖)- 在
setup()中使用computed和watch