Skip to content
第 4 章 前端 ⏱ 13 分钟阅读

第 4 章:computed / watch ​

学习目标 ​

  • 用 computed 派生数据
  • 区分 watch / watchEffect 适用场景
  • 控制 watch 的 immediate / deep / flush
  • 避开监听不到变化的 4 个坑

一、computed:派生数据(有缓存) ​

vue
<script setup lang="ts">
import { ref, computed } from 'vue'

const list = ref([1, 2, 3, 4])
const sum = computed(() => list.value.reduce((a, b) => a + b, 0))

const doubled = computed({
  get: () => list.value.map(x => x * 2),
  set: v => list.value = v.map(x => x / 2),  // 可写
})
</script>

<template>
  <p>合计:{{ sum }}</p>
  <p>双倍:{{ doubled }}</p>
</template>

computed 只在依赖变化时重算一次,模板多次访问不会重复执行。

⚠️ 坑 1:computed 里不要改其他响应式状态,会形成循环依赖。

二、watch:监听到变化后做事 ​

vue
<script setup lang="ts">
import { ref, watch } from 'vue'

const id = ref(1)
const user = ref<{ name: string } | null>(null)

// 1. 监听 ref
watch(id, (newVal, oldVal) => {
  console.log('id 改变', newVal, oldVal)
})

// 2. 立即执行 + 深度监听
watch(user, () => fetchUser(), {
  immediate: true,
  deep: true,
})

// 3. 监听多个源
watch([id, () => user.value?.name], ([nid, nname]) => {
  console.log(nid, nname)
})

三、watchEffect:自动收集依赖 ​

vue
<script setup lang="ts">
import { ref, watchEffect } from 'vue'

const url = ref('/api/list')
const data = ref(null)

watchEffect(async () => {
  // 自动追踪 url.value
  const res = await fetch(url.value)
  data.value = await res.json()
})

watchEffect 立即执行一次,体内用到的 ref 自动成为依赖。

四、flush 时机 ​

选项触发时机
'pre' (默认)组件更新前
'post'组件更新后
'sync'同步,极少用
ts
watch(source, cb, { flush: 'post' })    // DOM 已更新

五、停止监听 ​

ts
const stop = watchEffect(() => {...})
stop()                                    // 手动停止

// 或在 onScopeDispose 中停
import { getCurrentScope, onScopeDispose } from 'vue'
onScopeDispose(() => stop())

⚠️ 坑 2:watch 监听 reactive 对象时,默认 deep: true,无需再写。 ⚠️ 坑 3:监听 reactive 的基本类型字段要传函数:() => state.count,直接写 state.count 不会触发(因为值是基本类型,需要 getter)。 ⚠️ 坑 4:watch 回调里不要解构 reactive,会丢失响应性。

六、实战:防抖搜索 ​

vue
<script setup lang="ts">
import { ref, watch } from 'vue'

const keyword = ref('')
const result = ref([])

let timer: number | undefined
watch(keyword, (val) => {
  clearTimeout(timer)
  timer = setTimeout(async () => {
    result.value = await api.search(val)
  }, 300)
})
</script>

七、本章小结 ​

API用途
computed派生数据,有缓存,只读/可写
watch显式声明源,新旧值监听
watchEffect自动收集依赖,写法短
flush: 'post'DOM 更新后触发
immediate / deep立即执行 / 深度监听

动手练习 ​

  1. 价格计算:用 computed 实现 price * count + 运费
  2. 持久化:用 watch 监听 count,变化时 localStorage.setItem
  3. 搜索:用 watchEffect + setTimeout 实现 300ms 防抖

下一章:第 5 章:组件基础 →

本站基于 VitePress 构建 · 由 StackHub 团队维护