Skip to content

Vue 监听器

虽然计算属性在大多数情况下更合适,但有时我们需要在状态变化时执行“副作用”(side effects):例如更改 DOM,或者根据异步操作的结果去修改另一处的状态。在 Vue 中,我们可以使用监听器 (Watchers) 来响应数据的变化。

watch

watch 函数用于在某个响应式数据源发生变化时,执行一个回调函数。

基础用法

watch 接收三个参数:

  1. 一个想要监听的数据源 (可以是一个 ref,或一个返回值的 getter 函数)。
  2. 一个在数据源变化时执行的回调函数。回调函数接收新值和旧值作为参数。
  3. 一个可选的配置对象
vue
<script setup>
import { ref, watch } from 'vue'

const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')

// 可以直接监听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.indexOf('?') > -1) {
    answer.value = 'Thinking...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    }
  }
})
</script>

<template>
  <p>
    Ask a yes/no question:
    <input v-model="question" />
  </p>
  <p>{{ answer }}</p>
</template>

watch vs. computed

watchcomputed 都允许我们响应式地执行操作。主要的区别是它们的使用场景:

  • computed:用于派生新的数据。它是声明式的,返回一个值,并且有缓存。当你需要根据其他数据计算出一个新值时,使用计算属性。
  • watch:用于执行副作用。它是命令式的,不返回值。当你需要在数据变化时执行异步操作、操作 DOM 或与其他系统交互时,使用监听器。

深层监听

直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发。然而,这只在整个对象被替换时才会触发。

js
const obj = reactive({ count: 0 })

watch(obj, (newValue, oldValue) => {
  // 在 obj.count 改变时触发
})

如果你想在对象内部属性变化时触发,需要使用 deep: true 选项。

js
watch(
  () => state.someObject,
  (newValue, oldValue) => {
    // ...
  },
  { deep: true }
)

watchEffect

watchEffectwatch 的一个变体。它会立即执行一次回调函数,然后自动追踪其所有响应式依赖。当任何一个依赖发生变化时,它会重新运行该函数。

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

const todoId = ref(1)
const data = ref(null)

watchEffect(async () => {
  // 这个 effect 会立即运行,
  // 然后在 todoId.value 改变时重新运行。
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  )
  data.value = await response.json()
})
</script>

<template>
  <p>Todo id: {{ todoId }}</p>
  <button @click="todoId++">Fetch next todo</button>
  <p v-if="!data">Loading...</p>
  <pre v-else>{{ data }}</pre>
</template>

watch vs. watchEffect

  • 依赖追踪watch 只追踪明确指定的数据源,而 watchEffect 会自动追踪其回调函数中访问的所有响应式数据。
  • 惰性执行watch 默认是惰性的(只有在数据源变化时才执行),而 watchEffect 会在创建时立即执行一次。
  • 数据访问watch 可以访问变化前后的值,而 watchEffect 不能。

选择哪个取决于你的需求。如果你需要精确控制要监听的数据,或者需要访问旧值,请使用 watch。如果你的副作用逻辑依赖于多个响应式源,并且你不需要旧值,watchEffect 会更简洁。