Vue3 计算属性和侦听器

116 阅读5分钟

🌟 深入理解 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>

当你修改 firstNamelastName 时,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>

当你触发其他无关更新(比如弹窗状态变化)时:

  • 计算属性:只在 firstNamelastName 改变时打印日志
  • 方法:每次组件重新渲染都会打印日志!

💡 结论:对于依赖响应式数据的“派生值”,优先使用 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}"`)
})

✅ 优点:写法简单
❌ 缺点:无法配置 deepimmediate 等选项


2. 对象写法(推荐!功能完整)
watch(keyword, {
  handler(newVal, oldVal) {
    console.log(`关键词从 "${oldVal}" 变为 "${newVal}"`)
  },
  immediate: true,  // 立即执行一次
  deep: true        // 深度监听(对对象有效)
})

✅ 优点:

  • 可以同时设置 immediatedeep
  • 逻辑更清晰,适合复杂场景
  • 团队协作中更易维护

💡 实际开发建议:只记对象写法就够了!

很多开发者在项目中来回切换写法,容易出错。建议:统一使用对象写法,一劳永逸。

✅ 推荐模板(可复制粘贴):
watch(dataSource, {
  handler: async (newVal, oldVal) => {
    // 执行副作用:发请求、存 localStorage、更新状态等
    if (newVal !== oldVal) {
      await fetchData(newVal)
    }
  },
  immediate: true,  // 页面加载时立即执行一次
  deep: true        // 如果监听的是对象或数组,确保开启 deep
})

📌 即使你不需要 immediatedeep,也可以显式写上 false,提高代码可读性:

watch(form, {
  handler: (newVal) => {
    console.log('表单变化了')
  },
  immediate: false,
  deep: true  // 表单是对象,建议开启 deep
})

🎯 何时使用 deep: true

  • 监听对象内部属性变化时必须开启
  • 监听数组元素变化(如 pushsplice)时通常不需要 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:核心区别总结

特性computedwatch
用途生成一个新的响应式值执行副作用(如 API 请求、日志)
返回值必须返回一个值不返回值,用于“做事”
缓存机制✅ 有缓存,依赖不变不重新计算❌ 无缓存,每次变化都执行回调
性能高(惰性求值)中(可控制执行频率)
是否响应式是,返回值是响应式的否,回调函数本身不是响应式值

✅ 简单判断标准:

  • 想要一个(用于模板显示)? → 用 computed
  • 想要“做件事”(如发请求、存 localStorage)? → 用 watch

✅ 最佳实践建议

场景推荐方案
拼接字符串、过滤列表、计算数值computed
发送网络请求、保存数据到本地watch(对象写法)
频繁调用且依赖响应式数据的方法✅ 改为 computed
监听路由、表单、搜索关键词watch + immediate: true
监听对象深层变化watch + deep: true
避免在模板中调用方法进行计算❌ 应使用 computed 替代

🎯 结语

在 Vue 3 开发中,合理使用 computedwatch 能让你的代码更清晰、更高效。

💡 记住三句话:

  1. computed 是“我要一个值”
  2. watch 是“我要做件事”
  3. watch 只记对象写法,deepimmediate 直接写上,省心又安全

尤其是 computedmethods 的选择,直接影响应用性能。避免在模板中滥用方法进行计算,善用 computed 的缓存机制,是写出高性能 Vue 应用的关键。

现在,就去检查你的代码,把那些本该是 computed 的方法重构掉,并统一 watch 的写法吧!


📌 延伸阅读