VUE3-watchEffect 和 watch 相同点、区别、使用场景

3 阅读4分钟

watchEffect 和 watch 详解:相同点、区别、使用场景

在 Vue 3 组合式 API 中,watchwatchEffect 都是用来监听响应式数据变化并执行副作用的核心 API,它们底层依赖同一个响应式追踪系统,只是使用方式、追踪方式、适用场景不同。

一、相同点

  1. 核心作用一致:都是监听响应式数据ref/reactive/计算属性)变化,触发回调函数执行逻辑(如请求、DOM操作、数据修改)。
  2. 都支持停止监听:调用返回的停止函数,即可手动停止监听。
  3. 都支持清除副作用:回调中可以返回清理函数,在下次执行前/组件卸载时清理(如清除定时器、取消请求)。
  4. 都支持配置项:都可以配置 immediate(立即执行)、flush(执行时机)、deep(深度监听)等。
  5. 都在组件卸载时自动停止:无需手动处理内存泄漏。

二、核心区别(重点)

特性watchwatchEffect
追踪方式手动指定要监听的数据源自动追踪回调中用到的所有响应式数据
惰性执行默认惰性(数据变化才执行)默认非惰性(初始化立即执行一次)
新旧值可以同时获取 新值、旧值只能获取新值,无法获取旧值
监听目标可监听:单个值、多个值、对象属性、getter函数只能监听回调中直接使用的响应式数据
精准度高,只监听指定数据低,回调中所有依赖都会触发
使用风格命令式(明确写要监听谁)声明式(只写逻辑,自动依赖追踪)

三、详细用法对比

1. watch:手动指定数据源,精准监听

特点:必须明确告诉它「要监听谁」,适合需要精准控制、需要新旧值的场景。

基础用法
<script setup> 
    import { ref, watch } from 'vue' 
    const count = ref(0) 
    const user = reactive({ name: '张三' }) 
    // 1. 监听单个 ref 
    watch(count, (newVal, oldVal) => { 
        console.log('count变化', newVal, oldVal) 
    })
    // 2. 监听多个数据源(数组形式) 
    watch(
        [count, () => user.name], 
        ([newCount, newName], [oldCount, oldName]) =>{
            console.log('count或name变化') 
         }
    ) 
    // 3. 监听对象属性(必须用 getter 函数,否则监听不到) 
    watch( 
        () => user.name, 
        (newVal) => { 
            console.log('名字变化') 
        } 
     ) 
    // 4. 立即执行 + 深度监听 
    watch(user, () => {}, { immediate: true, deep: true }) 
</script> 

2. watchEffect:自动追踪依赖,简洁监听

特点不用手动指定数据源,执行时会自动收集回调中用到的所有响应式依赖,依赖变化就执行;初始化立即执行一次。

基础用法
<script setup> 
    import { ref, reactive, watchEffect } from 'vue' 
    const count = ref(0) 
    const user = reactive({ name: '李四' }) 
    // 自动监听 count 和 user.name 
    watchEffect(() => { 
        // 只要这里用到的响应式数据变了,就会重新执行 
        console.log('count:', count.value) 
        console.log('name:', user.name) 
     }) 
    // 模拟修改依赖,触发执行 
    count.value++ user.name = '王五' 
</script> 

四、使用场景(最实用的总结)

✅ 什么时候用 watch

  1. 需要获取 新值 + 旧值
  2. 只需要监听指定的某个/某几个数据(精准控制,避免不必要的执行)
  3. 监听的逻辑和数据源不是强绑定,需要灵活控制
  4. 监听复杂对象且需要深度监听、精准控制
  5. 不需要初始化立即执行(默认惰性)

典型场景

  • 路由参数变化,根据旧值/新值做不同逻辑
  • 表单某个字段变化,单独处理
  • 状态切换,需要对比前后值

✅ 什么时候用 watchEffect

  1. 监听的数据源不固定/多个,不想手动一个个写
  2. 希望初始化立即执行(比如一进页面就加载数据)
  3. 代码追求简洁,依赖自动追踪
  4. 不需要旧值,只需要响应依赖变化

典型场景

  • 监听多个查询条件,自动触发搜索
  • 一进页面就加载数据,依赖变化重新加载
  • 自动更新 DOM / 文档标题
  • 副作用逻辑依赖多个响应式数据

五、代码示例:最佳实践

1. watch 场景:需要新旧值

// 分页变化,需要对比页码,做滚动/请求优化 
const page = ref(1) 
watch(page, (newPage, oldPage) => { 
   if (newPage > oldPage) { 
      console.log('下一页,请求数据') 
   } else { 
     console.log('上一页,缓存数据') 
} getList(newPage) }) 

2. watchEffect 场景:自动追踪多依赖


// 搜索:关键词、分类、排序任意变化,自动搜索 
const keyword = ref('') 
const category = ref('') 
const sort = ref('') 
watchEffect(() => { 
    // 自动监听三个值,任意一个变了就执行 
    searchList({ 
        keyword: keyword.value, 
        category: category.value, 
        sort: sort.value 
     }) 
 }) 

六、补充:停止监听 + 清除副作用 两者用法完全一致:

// 停止监听 
const stop = watchEffect(...) stop() 
// 手动停止 // 清除副作用(定时器/请求) 
watchEffect((onCleanup) => { 
    const timer = setTimeout(() => {}, 1000) 
    // 下次执行前/卸载时自动清理 
    onCleanup(() => clearTimeout(timer)) 
}) 

总结

  1. 相同:都是监听响应式数据、处理副作用、支持停止/清理。
  2. 核心区别watch 手动指定数据源、可拿新旧值;watchEffect 自动追踪、简洁、无旧值。
  3. 使用口诀
    • 精准、要新旧值 → 用 watch
    • 简洁、自动追踪、立即执行 → 用 watchEffect