glownight

返回

一、核心区别#

特性watchwatchEffect
监听方式显式指定要监听的数据源自动追踪回调中用到的响应式数据
首次执行默认不执行,数据变化后才触发立即执行(类似 computed
获取旧值✅ 可以获取 oldValue❌ 无法获取旧值
使用场景需要对比新旧值、条件触发副作用逻辑(如日志、DOM 操作)
写法需要指定数据源无需指定,自动依赖收集

二、watch 详解#

基本用法#

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

const count = ref(0)
const name = ref('Vue')

// 监听单个 ref
watch(count, (newVal, oldVal) => {
  console.log('count 变化:', oldVal, '→', newVal)
})

// 监听多个数据源
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
  console.log('多个数据变化:', newCount, newName)
})

// 监听 getter 函数(监听对象属性)
const user = ref({ age: 20 })
watch(
  () => user.value.age,
  (newAge, oldAge) => {
    console.log('age 变化:', oldAge, '→', newAge)
  }
)
</script>
vue

配置选项#

watch(source, callback, {
  immediate: true,   // 首次立即执行(默认 false)
  deep: true,        // 深度监听对象内部变化(默认 false)
  flush: 'post'      // 组件更新后执行(默认 'pre')
})
javascript

深度监听#

const state = reactive({ 
  nested: { count: 0 } 
})

// ❌ 浅监听:只监听引用变化
watch(state.nested, (newVal, oldVal) => {
  console.log('不会触发,除非整个对象替换')
})

// ✅ 深度监听
watch(
  () => state.nested,
  (newVal, oldVal) => {
    console.log('嵌套属性变化也会触发')
  },
  { deep: true }
)
javascript

⚠️ 深度监听性能开销大,尽量精确监听具体属性。


三、watchEffect 详解#

基本用法#

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

const count = ref(0)
const name = ref('Vue')

// 自动追踪用到的响应式数据
watchEffect(() => {
  console.log('当前 count:', count.value)
  console.log('当前 name:', name.value)
  // 只要 count 或 name 变化,就会重新执行
})

// 首次立即输出:
// 当前 count: 0
// 当前 name: Vue
</script>
vue

清理副作用#

const id = ref(1)

watchEffect((onCleanup) => {
  const controller = new AbortController()
  
  fetch(`/api/user/${id.value}`, { signal: controller.signal })
    .then(res => res.json())
    .then(data => console.log(data))
  
  // 下次执行前或组件卸载时调用
  onCleanup(() => {
    controller.abort()  // 取消之前的请求
  })
})
javascript

停止监听#

const stop = watchEffect(() => {
  console.log(count.value)
})

// 手动停止
stop()
javascript

四、对比示例#

场景:监听搜索关键词#

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

const keyword = ref('')
const results = ref([])

// ✅ watch:适合有明确数据源,需要对比新旧值
watch(keyword, async (newVal, oldVal) => {
  if (newVal.length < 2) {
    results.value = []
    return
  }
  const data = await fetchSearch(newVal)
  results.value = data
}, { debounce: 300 })  // 可配合防抖

// ✅ watchEffect:适合副作用逻辑,自动追踪依赖
watchEffect(() => {
  console.log(`搜索日志: 用户输入 "${keyword.value}"`)
  document.title = keyword.value 
    ? `搜索: ${keyword.value}` 
    : '首页'
})
</script>
vue

五、何时用哪个?#

┌─────────────────────────────────────────────────────────┐
│  需要获取旧值做对比?                                     │
│  例如:撤销功能、表单对比                                  │
└────────────┬────────────────────────────────────────────┘

      ┌──────┴──────┐
     是              否
      │               │
      ▼               ▼
   用 watch      副作用逻辑?

              ┌─────┴─────┐
             是           否
              │            │
              ▼            ▼
          watchEffect   明确数据源?

                    ┌──────┴──────┐
                   是             否
                    │              │
                    ▼              ▼
                 用 watch      用 watchEffect
plaintext
watchwatchEffect
需要 oldValue副作用逻辑(日志、DOM、标题)
需要条件判断(如防抖)依赖关系复杂,不想手动维护
监听特定属性需要立即执行
需要 deep 深度监听请求取消等清理副作用

六、常见误区#

误区 1:watch 监听 reactive 对象#

const state = reactive({ count: 0 })

// ❌ 错误:监听的是 reactive 对象,不是 ref
watch(state, () => {})  // 可以工作,但无法正确获取旧值

// ✅ 正确:监听 getter 或转为 ref
watch(() => state.count, (newVal, oldVal) => {})
watch(toRef(state, 'count'), (newVal, oldVal) => {})
javascript

误区 2:watchEffect 中遗漏依赖#

const count = ref(0)
const name = ref('Vue')

watchEffect(() => {
  console.log(count.value)  // 只追踪了 count
  // name 变化不会触发!
})
javascript

误区 3:异步回调中的旧值陷阱#

watch(count, async (newVal, oldVal) => {
  await someAsyncOp()  // 等待期间 count 可能又变了
  console.log(oldVal)  // 还是正确的旧值 ✅
})
javascript

七、Vue 2 vs Vue 3 的 watch#

Vue 2 (Options API)Vue 3 (Composition API)
写法watch: { count() {} }watch(count, () => {})
立即执行immediate: trueimmediate: true
深度监听deep: truedeep: true
多数据源需要写多个 watcherwatch([a, b], ...)
watchEffect❌ 无✅ 有

八、总结#

watchwatchEffect
本质命令式:你告诉 Vue 监听什么声明式:Vue 自动发现依赖
心智模型”当 X 变化时,做 Y""这段代码依赖了哪些数据,就自动重跑”
灵活性高(精确控制)低(自动但不可控)
性能可控(只监听需要的数据)需注意(可能追踪过多依赖)

一句话记忆

watch精准狙击——你指定目标,它才开火;
watchEffect自动雷达——它自己扫描范围内的所有响应式数据,谁动打谁。

watch 和 watchEffect
作者 glownight
发布于 2026年4月27日