Skip to content

基本用法

基本示例

watch 函数在每次响应式状态变化时触发回调函数,可以执行一些“副作用”,例如:更改 DOM 元素、异步请求。

vue
<template>
  <div class="div-box">
    <p>
      Ask a yes/no question:
      <input v-model="question" :disabled="loading" />
    </p>
    <p>{{ answer }}</p>
  </div>
</template>

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

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

watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      const res = await fetch(`https://yesno.wtf/api`)
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'Error fetching data. Please try again later.'
    } finally {
      loading.value = false
    }
  }
})
</script>

回调的触发时机

TIP

默认情况下,侦听器回调会在父组件更新 (如有) 之后、所属组件的 DOM 更新之前被调用。

Vue 更新之前触发

注意

{ flush: "sync" } 每当检测到响应式数据变化时,都会立即执行回调函数。 可以用来监视简单值(例如:布尔值),但应避免在可能多次同步修改的数据源(例如:数组)上使用。

js
watch(source, callback, {
  flush: "sync",
});

watchEffect(callback, {
  flush: "sync",
});
js
import { watchSyncEffect } from "vue";

watchSyncEffect(() => {
  /* 在响应式数据变化时同步执行 */
});

Vue 更新之后触发

js
watch(source, callback, {
  flush: "post",
});

watchEffect(callback, {
  flush: "post",
});
js
import { watchPostEffect } from "vue";

watchPostEffect(() => {
  /* 在 Vue 更新后执行 */
});

副作用清理

onCleanup Vue3.5 之前

js
watch(id, (newId, oldId, onCleanup) => {
  // ...
  onCleanup(() => {
    // 清理逻辑
  });
});

watchEffect((onCleanup) => {
  // ...
  onCleanup(() => {
    // 清理逻辑
  });
});

onWatcherCleanup Vue3.5+

当侦听器失效并准备重新调用时,onWatcherCleanup会在下一次调用之前执行。

注意

onWatcherCleanup 仅在 Vue3.5+ 中支持。 并且必须在 watchwatchEffect 回调函数的同步执行期间调用,不能在异步函数的await语句之后调用。

js
import { watch, onWatcherCleanup } from "vue";

watch(id, (newId) => {
  const controller = new AbortController();

  fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
    // 回调逻辑
  });

  onWatcherCleanup(() => {
    // 终止过期请求
    controller.abort();
  });
});

停止侦听

使用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,无需关心怎么停止侦听器。

如果使用异步回调创建一个侦听器,就必须手动停止它,以防止内存泄漏。

vue
<script setup>
import { watchEffect } from "vue";

// 它会自动停止
watchEffect(() => {});

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {});
}, 100);
</script>

要手动停止一个侦听器,可以调用 watchwatchEffect 返回的函数:

js
const unwatch = watchEffect(() => {});

// ...当该侦听器不再需要时
unwatch();

注意

需要异步创建侦听器的情况很少,应尽可能选择同步创建。 如果需要等待一些异步数据,可以使用条件式的侦听逻辑:

js
// 需要异步请求得到的数据
const data = ref(null);

watchEffect(() => {
  if (data.value) {
    // 数据加载后执行某些操作...
  }
});