前端八股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
相关推荐
Avan_菜菜17 小时前
AI 能写代码了,为什么我反而开始要求它先写文档?
前端·github·ai编程
JieE21220 小时前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE21221 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
爱勇宝21 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒1 天前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen1 天前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
Larcher1 天前
AI Loop:让AI像人一样自主完成任务的核心机制
javascript·人工智能·设计模式
默_笙1 天前
🃏 JS 只有 8 种数据类型,但我花了 2 天才搞懂 null 和 undefined 的区别
javascript
牧艺1 天前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
jump_jump1 天前
流式 HTML:从 htmx 片段装配到浏览器原生增量渲染
javascript·性能优化·前端工程化