前端八股Vue(7)---computed计算属性和watch侦听器

目录

一、一句话核心区别

二、computed(计算属性)详解

[2.1 一句话定义](#2.1 一句话定义)

[2.2 特点](#2.2 特点)

[2.3 基本用法](#2.3 基本用法)

[2.4 适用场景](#2.4 适用场景)

三、watch(侦听器)详解

[3.1 一句话定义](#3.1 一句话定义)

[3.2 特点](#3.2 特点)

[3.3 基本用法](#3.3 基本用法)

[3.4 watch 的配置选项](#3.4 watch 的配置选项)

[3.5 适用场景](#3.5 适用场景)

四、核心区别对比表

五、实际场景选择指南

[什么时候用 computed?](#什么时候用 computed?)

[什么时候用 watch?](#什么时候用 watch?)

六、问答

[Q:computed 和 watch 的区别是什么?](#Q:computed 和 watch 的区别是什么?)

[问:为什么 watch 监听 reactive 对象时,newVal 和 oldVal 相等?](#问:为什么 watch 监听 reactive 对象时,newVal 和 oldVal 相等?)


一、一句话核心区别

维度 computed watch
核心定位 用已有的数据,算出一个新的数据 监听一个数据,它一变就做事情
有无缓存 ✅ 有缓存 ❌ 无缓存
是否 return ✅ 必须 return ❌ 不需要 return
能否异步 ❌ 不能(只能同步) ✅ 可以(发请求、定时器)
触发时机 依赖变化时自动重新计算 监听的数据变化时执行

二、computed(计算属性)详解

2.1 一句话定义

用已有的数据,算出一个新的数据。

2.2 特点

特点 说明
依赖响应式数据 依赖的变量变化时,自动重新计算
有缓存 依赖不变,计算结果不变,不会重复执行
必须 return 返回计算后的值
只能同步 不能写异步代码(不能发请求、定时器)
默认只读 也可以写 setter 实现可写

2.3 基本用法

javascript 复制代码
<template>
  <div>
    <p>firstName: {{ firstName }}</p>
    <p>lastName: {{ lastName }}</p>
    <p>fullName: {{ fullName }}</p>
    <p>totalPrice: {{ totalPrice }}</p>
    <p>filteredList: {{ filteredList }}</p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('张')
const lastName = ref('三')
const price = ref(100)
const quantity = ref(2)
const list = ref([1, 2, 3, 4, 5])

// 基本计算:拼接
const fullName = computed(() => {
  return firstName.value + lastName.value
})

// 计算属性:总价
const totalPrice = computed(() => {
  return price.value * quantity.value
})

// 数组过滤
const filteredList = computed(() => {
  return list.value.filter(item => item > 2)
})

// 可写的计算属性(少见,但面试可能会问)
const writableFullName = computed({
  get() {
    return firstName.value + ' ' + lastName.value
  },
  set(newValue) {
    const [first, last] = newValue.split(' ')
    firstName.value = first
    lastName.value = last
  }
})
</script>

2.4 适用场景

场景 示例
数据拼接 全名、地址拼接
数据计算 总价、平均值、数量统计
数据过滤 筛选列表、搜索过滤
数据格式化 日期格式化、金额格式化
多个数据组合 多个条件组合判断

三、watch(侦听器)详解

3.1 一句话定义

监听一个数据,它一变,我就做一些事情。

3.2 特点

特点 说明
监听变化 监听指定数据,变化时执行回调
无缓存 每次变化都会执行
不需要 return 执行副作用操作
可以做异步 发请求、定时器、防抖等
可以获取新旧值 回调参数 (newVal, oldVal)

3.3 基本用法

javascript 复制代码
<template>
  <div>
    <input v-model="searchKeyword" placeholder="搜索关键词" />
    <input v-model="user.name" placeholder="用户名" />
    <input v-model="user.age" placeholder="年龄" />
  </div>
</template>

<script setup>
import { ref, reactive, watch } from 'vue'

const searchKeyword = ref('')
const user = reactive({
  name: '张三',
  age: 18
})

// 1. 监听单个 ref
watch(searchKeyword, (newVal, oldVal) => {
  console.log(`搜索词从 ${oldVal} 变为 ${newVal}`)
  // 防抖搜索请求
  const timer = setTimeout(() => {
    // 发请求搜索
    fetchSearchResults(newVal)
  }, 500)
  return () => clearTimeout(timer)
})

// 2. 监听多个数据(数组形式)
watch([() => user.name, () => user.age], ([newName, newAge], [oldName, oldAge]) => {
  console.log(`name: ${oldName} → ${newName}`)
  console.log(`age: ${oldAge} → ${newAge}`)
})

//为什么用 () => user.name 而不是 user.name
// ❌ 错误:user.name 是字符串,不是响应式数据
//watch([user.name, user.age], (...) => {...})
// ✅ 正确:用 getter 函数获取响应式值
//watch([() => user.name, () => user.age], (...) => {...})

// 3. 监听 reactive 对象(需要深度监听)
watch(() => user, (newVal, oldVal) => {
  console.log('user 变化了', newVal)
}, { deep: true })  // 深度监听

// 4. 立即执行(页面初始化时立即执行一次)
watch(searchKeyword, (newVal) => {
  console.log('立即执行一次', newVal)
}, { immediate: true })

// 5. 异步操作
watch(searchKeyword, async (newVal) => {
  if (!newVal) return
  const res = await api.search(newVal)
  searchResults.value = res.data
})
</script>

3.4 watch 的配置选项

选项 说明
deep: true 深度监听,对象内部属性变化也能检测到
immediate: true 立即执行一次,页面初始化时就触发
flush: 'post' DOM 更新后再执行回调

3.5 适用场景

场景 示例
异步请求 搜索框输入变化发请求、id 变化重新获取数据
复杂逻辑 多个数据变化触发复杂业务逻辑
DOM 操作 数据变化后需要操作 DOM
路由监听 监听路由参数变化,重新加载页面数据
防抖/节流 输入框防抖搜索

四、核心区别对比表

对比维度 computed watch
本质 计算新值 监听变化做事情
是否有缓存 ✅ 有(依赖不变,结果不变) ❌ 无(每次变化都执行)
是否必须 return ✅ 是 ❌ 否
能否异步 ❌ 否 ✅ 是
是否支持深度监听 ❌ 否(自动收集依赖) ✅ 是(需要 deep: true)
是否支持立即执行 ❌ 否(懒执行) ✅ 是(immediate: true)
返回值 返回计算值 无返回值,执行副作用
使用方式 模板中直接使用 监听数据变化执行回调

五、实际场景选择指南

什么时候用 computed?

javascript 复制代码
// ✅ 场景1:数据拼接
const fullName = computed(() => firstName.value + ' ' + lastName.value)

// ✅ 场景2:数据计算
const totalPrice = computed(() => price.value * quantity.value)

// ✅ 场景3:数组过滤
const activeUsers = computed(() => users.value.filter(u => u.isActive))

// ✅ 场景4:数据格式化
const formattedDate = computed(() => dayjs(date.value).format('YYYY-MM-DD'))

什么时候用 watch?

javascript 复制代码
// ✅ 场景1:异步请求
watch(keyword, async (newVal) => {
  const res = await api.search(newVal)
  results.value = res.data
})

// ✅ 场景2:复杂业务逻辑
watch(formData, (newVal) => {
  // 表单变化时,校验、保存草稿等
  validateForm(newVal)
  saveDraft(newVal)
}, { deep: true })

// ✅ 场景3:路由变化监听
watch(() => route.params.id, (newId) => {
  fetchDetail(newId)
})

// ✅ 场景4:DOM 操作
watch(showModal, (newVal) => {
  if (newVal) {
    // 弹窗打开后,聚焦输入框
    nextTick(() => inputRef.value.focus())
  }
})

六、问答

Q:computed 和 watch 的区别是什么?

答: computed 和 watch 的核心区别在于:

1. 定位不同

  • computed 是计算属性,根据已有数据计算新值,必须 return

  • watch 是侦听器,监听数据变化后执行副作用操作,不需要 return

2. 缓存机制

  • computed 有缓存,依赖不变就不会重新计算

  • watch 没有缓存,每次变化都执行

3. 异步支持

  • computed 只能同步,不能写异步代码

  • watch 支持异步,可以发请求、开定时器

4. 使用场景

  • computed 适合:数据拼接、计算、过滤、格式化

  • watch 适合:异步请求、复杂逻辑、DOM 操作、路由监听

问:为什么 watch 监听 reactive 对象时,newVal 和 oldVal 相等?

答: 因为 reactive 返回的是 Proxy 代理对象,修改的是对象内部的属性,而不是对象本身的引用。所以新旧值指向的是同一个 Proxy 对象,因此它们全等。如果需要获取真正的旧值,应该监听具体的属性,而不是整个对象。


复制代码
computed 算新值,有缓存,必须 return,不能异步
watch 做事情,无缓存,不用 return,可以异步

数据拼接用 computed
发请求用 watch
过滤列表用 computed
表单验证用 watch
相关推荐
REDcker19 分钟前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
donecoding2 小时前
一个 sudo 引发的血案:npm 全局包权限错乱彻底修复
前端·node.js·前端工程化
风骏时光牛马2 小时前
Raku正则匹配与数据批量处理实操案例
前端
nbwenren2 小时前
2026实测:Gemini 3 镜像站视觉能力实践——拍照原型图,一键生成 HTML+CSS 代码
前端·css·html
Lee川2 小时前
Prisma 实战指南:像搭积木一样设计古诗词数据库
前端·数据库·后端
Linsk2 小时前
Java和JavaScript的关系真是雷峰和雷峰塔的关系吗?
java·javascript·oracle
当时只道寻常2 小时前
浏览器文本复制到剪贴板:企业级最佳实践
javascript
jinanwuhuaguo2 小时前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw
广州华水科技2 小时前
深度测评2026年单北斗GNSS位移监测系统推荐,与高口碑变形监测设备一同引领行业新风尚
前端