技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTVUE

Vue watch 监听 computed 属性?

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/15-vue watch监听computed属性?
本文目录7 个章节
  1. 结论
  2. Vue 2 的 Options API 写法
  3. Vue 3 的 Composition API 写法
  4. 监听什么时候触发?
  5. watch 和 watchEffect 的选择
  6. 小结
  7. 参考

Vue watch 监听 computed 属性?

本地原文仅保留了一个 SegmentFault 链接,下面补充这个问题的完整答案,并保留原始出处。示例同时覆盖 Vue 2 Options API 和 Vue 3 Composition API。

结论

可以监听 computed 属性。computed 负责根据响应式依赖计算并缓存一个派生值,watch 负责在该值发生变化时执行副作用,例如请求接口、记录日志或同步外部状态。

Vue 2 的 Options API 写法

Vue 2 中可以直接在 watch 选项里使用 computed 属性名:

export default {
  data() {
    return {
      firstName: 'Ada',
      lastName: 'Lovelace'
    }
  },
  computed: {
    fullName() {
      return `${this.firstName} ${this.lastName}`
    }
  },
  watch: {
    fullName(newValue, oldValue) {
      console.log('fullName changed:', oldValue, '=>', newValue)
    }
  }
}

也可以使用实例方法:

this.$watch('fullName', (newValue, oldValue) => {
  // 监听 computed 属性的结果
})

Vue 2.7 仍然保留这套写法,但它的底层响应式实现仍是 getter/setter、DepWatcher,不是 Vue 3 的 Proxy。

Vue 3 的 Composition API 写法

在 Vue 3 中,computed() 返回的是 computed ref,可以直接作为 watch 的来源:

import { computed, ref, watch } from 'vue'

const firstName = ref('Ada')
const lastName = ref('Lovelace')

const fullName = computed(() => `${firstName.value} ${lastName.value}`)

const stop = watch(fullName, (newValue, oldValue) => {
  console.log('fullName changed:', oldValue, '=>', newValue)
})

firstName.value = 'Grace'
// 触发 watch:Ada Lovelace => Grace Lovelace

// 不再需要时可以停止监听
// stop()

也可以显式使用 getter:

watch(
  () => fullName.value,
  (newValue, oldValue) => {
    console.log(newValue, oldValue)
  }
)

不要传入 computedRef.value

下面的写法通常是错误的:

watch(fullName.value, callback)

如果 fullName.value 是字符串或数字,这只是把当前普通值传给 watch,不会建立响应式依赖。应传入 computed ref 本身,或传入返回其值的 getter:

watch(fullName, callback)
// 或
watch(() => fullName.value, callback)

监听什么时候触发?

  • watch 默认是惰性的,创建时不会立即执行;需要初始化时执行可使用 { immediate: true }
  • 只有 computed 的结果发生变化时,普通 watch 回调才会触发。computed 的依赖发生变化但计算结果相同,不应把它理解成一定会触发回调。
  • 如果 computed 返回对象,监听 computed ref 默认关注对象引用;要观察嵌套属性,需要监听具体 getter,或谨慎使用 { deep: true }
  • computed getter 应保持纯净,不应在其中发请求、修改其他状态或操作 DOM;这些副作用应放进 watchwatchEffect
const query = ref('vue')
const result = ref(null)

watch(query, async (newQuery, oldQuery, onCleanup) => {
  const controller = new AbortController()
  onCleanup(() => controller.abort())

  const response = await fetch(`/api/search?q=${encodeURIComponent(newQuery)}`, {
    signal: controller.signal
  })
  result.value = await response.json()
})

Vue 3.5+ 还提供 onWatcherCleanup();异步回调中更通用的写法仍是使用回调的第三个 onCleanup 参数,并在 await 之前完成注册。

watchwatchEffect 的选择

如果来源需要明确控制,使用 watch

watch(fullName, () => {
  // 只在 fullName 结果变化时执行
})

如果副作用中读取的响应式依赖就是全部需要追踪的依赖,可以使用 watchEffect

import { watchEffect } from 'vue'

watchEffect(() => {
  console.log(fullName.value)
})

watchEffect 会立即执行,并自动追踪同步执行阶段读取的依赖;异步函数第一次 await 之后读取的属性不会被自动追踪。

小结

computed 可以被 watch。最重要的区别是:

  • Vue 2:在 watch 选项中写 computed 属性名,或调用 vm.$watch()
  • Vue 3:监听 computed() 返回的 ref,不要把 .value 的当前快照传进去。
  • computed 用于派生状态和缓存,watch 用于副作用和异步逻辑。

参考

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS