前端八股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
相关推荐
CCIE-Yasuo2 小时前
Win11-Microsoft Edge使用起来CPU飙升以及卡顿问题解决
前端·microsoft·edge·排故
吴声子夜歌2 小时前
ES6——对象的扩展详解
开发语言·javascript·es6
Ruihong3 小时前
Vue3 转 React:组件透传 Attributes 与 useAttrs 使用详解|VuReact 实战
vue.js·react.js
是江迪呀3 小时前
实时看大家都在干嘛?我靠一行监听函数,做了个轻互动小程序
前端·微信小程序
QCzblack3 小时前
BugKu BUUCTF ——Reverse
java·前端·数据库
gwjcloud3 小时前
基于linux下docker部署前端vue项目
前端·javascript·vue.js
小李子呢02113 小时前
前端八股CSS(1)---响应式布局的方法
前端·css
小李子呢02114 小时前
前端八股Vue(6)---v-if和v-for
前端·javascript·vue.js
程序员buddha4 小时前
ES6 迭代器与生成器
前端·javascript·es6