Vue 3 响应式进阶:watch 与 watchEffect 的触发时机、深度监听与性能取舍

test12026-09-273 次阅读

两种副作用 API 的区别

Vue 3 的 Composition API 提供 watch 和 watchEffect 两种侦听手段。核心差别在于:watch 需要你显式写出要监听的源,回调只在源变化时触发;watchEffect 则在函数体里自动收集所有读到的响应式依赖,首次立即执行一次,之后任何依赖变化都会重跑。

watch:显式指定依赖

watch 的第一个参数可以是 ref、reactive 的 getter,或它们的数组。回调拿到新旧值,适合需要知道变化前后内容的场景。

import { ref, watch } from "vue";

const keyword = ref("");

watch(
  keyword,
  (newVal, oldVal) => {
    console.log(`关键词从 ${oldVal} 变成 ${newVal}`);
    fetchResults(newVal);
  },
  { debounce: false } // 真实项目里可叠加防抖
);

监听 reactive 对象的某个字段时,用 getter 精确指向,避免整对象触发。

import { reactive, watch } from "vue";

const form = reactive({ name: "", age: 0 });

watch(
  () => form.age,
  (age) => {
    if (age < 0) form.age = 0;
  }
);

watchEffect:自动追踪

当你只关心“某些响应式数据变了就重算”,而不关心旧值,watchEffect 更简洁。它会立即跑一次以建立依赖。

import { ref, watchEffect } from "vue";

const width = ref(800);
const height = ref(600);

watchEffect(() => {
  // 读取 width、height,自动成为依赖
  document.title = `画布 ${width.value}x${height.value}`;
});

深度监听的代价

监听一个 reactive 对象时,默认就是深度监听,任意嵌套属性变动都会触发回调。这会带来递归遍历开销,对象越大越明显。

  • 只关心顶层字段,就传 getter 而非整个对象。
  • 确实需要深层监听普通 ref 包裹的对象时,显式打开 deep: true。
  • 深层监听大列表时,考虑改成只监听真正影响逻辑的字段。
const state = ref({ list: [{ id: 1, on: false }] });

// 不必要地监听整个深层结构
watch(state, cb, { deep: true });

// 更省:只监听派生结果
watch(
  () => state.value.list.filter((i) => i.on).length,
  cb
);

停止监听与清理

watch 和 watchEffect 都会返回停止函数,在组件卸载或手动取消时调用,防止内存泄漏。watchEffect 还能在回调里返回一个清理函数,用于取消上一次副作用。

import { watchEffect, onBeforeUnmount } from "vue";

const stop = watchEffect((onCleanup) => {
  const timer = setInterval(() => {}, 1000);
  onCleanup(() => clearInterval(timer));
});

onBeforeUnmount(() => stop());
  • 需要旧值或精确控制源,用 watch。
  • 只想“依赖一变就重跑”,用 watchEffect 更短。
  • 深度监听有成本,能用 getter 收窄就别监听整对象。
T

test1

文章作者

两种副作用 API 的区别 Vue 3 的 Composition API 提供 watch 和 watchEffec...

分类
技术
发布时间
2026-09-27
字数
约 1743 字
阅读
3 次