Vue计算属性与监视
一、计算属性:computed
1. 什么是 computed?
computed
是 Vue 提供的一种声明式的数据处理方式。它基于响应式依赖进行缓存,只有当依赖的数据发生变化时,才会重新计算。
✅ 核心特点:有缓存、性能高、适合复杂逻辑计算
2. 基本用法
<template><div><p>姓:{{ firstName }}</p><p>名:{{ lastName }}</p><p>全名:{{ fullName }}</p></div>
</template><script>
export default {data() {return {firstName: '张',lastName: '三'}},computed: {fullName() {console.log('computed 执行了') // 仅在 firstName 或 lastName 变化时执行return this.firstName + ' ' + this.lastName}}
}
</script>
3. 计算属性 vs 方法(methods)
对比项 | computed | methods |
---|---|---|
是否缓存 | ✅ 有缓存(依赖不变不重新执行) | ❌ 每次渲染都执行 |
性能 | 高(适合复杂计算) | 低(频繁调用影响性能) |
使用场景 | 依赖数据派生新值 | 简单逻辑或带参数操作 |
<!-- 使用 methods(不推荐用于计算) -->
<p>全名:{{ getFullName() }}</p><script>
methods: {getFullName() {console.log('methods 执行了') // 每次渲染都打印return this.firstName + ' ' + this.lastName}
}
</script>
🔍 结论:只要涉及“依赖数据生成新值”,优先使用
computed
二、计算属性的 setter 与 getter
默认情况下,computed
只有 getter,但你也可以定义 setter,实现“反向绑定”。
computed: {fullName: {// getterget() {return this.firstName + ' ' + this.lastName},// setterset(newValue) {const names = newValue.split(' ')this.firstName = names[0]this.lastName = names[1] || ''}}
}
此时,当你修改 fullName
时,会自动拆分并更新 firstName
和 lastName
:
this.fullName = '李 四' // 触发 setter
// 结果:firstName = '李', lastName = '四'
💡 这种用法较少见,但在表单联动等场景中非常有用。
三、侦听器:watch
1. 什么是 watch?
watch
用于监听某个数据的变化,并在变化时执行副作用操作,比如:
- 发送网络请求
- 打印日志
- 执行异步操作
- 手动更新 DOM
✅ 核心特点:响应式监听、适合副作用操作
2. 基本语法
export default {data() {return {keyword: ''}},watch: {keyword(newVal, oldVal) {console.log(`搜索词从 ${oldVal} 变为 ${newVal}`)// 模拟搜索请求if (newVal) {this.searchData(newVal)}}},methods: {searchData(keyword) {// 调用 APIconsole.log('请求接口:', `/api/search?q=${keyword}`)}}
}
3. 深度监听(deep watch)
默认 watch
只监听引用变化,不监听对象内部属性变化。要监听深层变化,需启用 deep: true
。
data() {return {user: {name: 'Alice',age: 25}}
},
watch: {user: {handler(newVal, oldVal) {console.log('用户信息变化了')},deep: true // 深度监听}
}
4. 立即执行(immediate)
希望监听器在创建时立即执行一次?使用 immediate: true
。
watch: {keyword: {handler(newVal) {this.searchData(newVal)},immediate: true // 页面加载时立即执行}
}
🌟 常用于:页面初始化时根据路由参数或状态自动加载数据。
四、computed vs watch:如何选择?
场景 | 推荐使用 | 说明 |
---|---|---|
根据数据生成新值(如拼接、过滤、计算) | ✅ computed | 有缓存,性能高 |
执行异步操作或复杂逻辑(如请求接口) | ✅ watch | 更适合副作用 |
需要监听变化并执行多个步骤 | ✅ watch | 逻辑更清晰 |
需要立即执行一次 | ✅ watch (配合 immediate ) | computed 不支持 |
监听路由或全局状态变化 | ✅ watch | 更灵活 |
✅ 使用口诀:
“能用 computed 的,绝不用 watch;需要副作用的,用 watch。”
五、Vue 3 Composition API 写法
在 setup()
中,computed
和 watch
变成了独立的函数,使用更灵活。
1. computed 的 Composition 写法
import { ref, computed } from 'vue'export default {setup() {const firstName = ref('张')const lastName = ref('三')const fullName = computed(() => {return firstName.value + ' ' + lastName.value})return { firstName, lastName, fullName }}
}
2. watch 的 Composition 写法
import { ref, watch } from 'vue'export default {setup() {const keyword = ref('')watch(keyword, (newVal, oldVal) => {console.log(`搜索词变化:${oldVal} → ${newVal}`)})// 深度监听const user = ref({ name: 'Alice' })watch(user, (newVal) => {console.log('用户变化')}, { deep: true, immediate: true })return { keyword, user }}
}
3. 监听多个数据
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {console.log('姓名变化')
})
六、常见问题与最佳实践
❓ 1. computed 能监听路由变化吗?
不能直接监听 $route
,但可以通过 watch
监听 $route
变化,或在 setup
中使用 useRoute
配合 watch
。
// Vue 3
import { useRoute, watch } from 'vue-router'const route = useRoute()
watch(() => route.path, (newPath) => {console.log('路由变化:', newPath)
})
❓ 2. computed 中可以写异步操作吗?
不推荐! computed
应是同步的、无副作用的。异步操作请使用 watch
或 methods
。
❓ 3. watch 监听数组或对象时要注意什么?
- 监听数组时,
push
、pop
等方法会触发变化(Vue 能劫持这些方法)。 - 但直接通过索引修改(
arr[0] = val
)不会触发,应使用Vue.set
或splice
。 - 推荐使用
deep: true
进行深度监听。
七、性能优化建议
- 避免在
computed
中做耗时操作,虽然有缓存,但首次计算仍会影响渲染。 - 合理使用
deep
和immediate
,避免不必要的性能开销。 - 复杂逻辑拆分:将大
computed
拆分为多个小计算属性,提高可维护性。 - watch 后手动停止(高级用法):
const stopWatch = watch(source, callback)
// 不再需要时
stopWatch()
八、总结
特性 | computed | watch |
---|---|---|
是否缓存 | ✅ 是 | ❌ 否 |
适用场景 | 派生数据、模板渲染 | 副作用、异步操作 |
性能 | 高 | 中 |
是否支持异步 | ❌ 不推荐 | ✅ 支持 |
是否支持 immediate | ❌ 否 | ✅ 是 |
是否支持 deep | ❌(自动) | ✅ 是 |
Composition API | computed(() => {}) | watch(source, callback) |
九、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!