watcheffect 是 vue 3 中自动追踪响应式依赖并执行副作用的工具,无需手动指定依赖数组,适用于“参数变→请求发”场景,支持异步、防抖及竞态控制。

watchEffect 是 Vue 3 中非常实用的响应式副作用监听工具,它能自动追踪内部用到的响应式数据,并在它们变化时重新执行。相比 watch,它更简洁、更“自动”,特别适合「参数变 → 请求发」这类场景。
核心逻辑:依赖即参数,执行即请求
只要把请求逻辑写在 watchEffect 回调里,并在其中直接读取响应式参数(如 ref 或 reactive 的字段),Vue 就会自动建立依赖关系。参数一更新,请求立刻重发——不用手动写依赖数组,也不用处理首次执行问题。
- 参数必须是响应式数据(
ref、computed、reactive属性等) - 请求函数(如
fetchData)应定义在回调内或外部,但调用时必须用到被追踪的值 - 支持异步,但注意避免竞态问题(见下文)
基础写法:单个 ref 参数
比如根据用户 ID 自动拉取用户信息:
import { ref, watchEffect } from 'vue'
const userId = ref(1)
const userInfo = ref(null)
const loading = ref(false)
watchEffect(async (onInvalidate) => {
loading.value = true
const prevCall = { aborted: false }
onInvalidate(() => {
prevCall.aborted = true
})
try {
const res = await fetch(`/api/user/${userId.value}`)
if (!prevCall.aborted) {
userInfo.value = await res.json()
}
} catch (err) {
if (!prevCall.aborted) {
console.error(err)
}
} finally {
if (!prevCall.aborted) {
loading.value = false
}
}
})
✅ 每次 userId.value 改变,自动触发新请求
✅ onInvalidate 可拦截过期请求,防止结果覆盖
多参数 + 解构 reactive 场景
当参数来自一个响应式对象(如搜索表单):
import { reactive, watchEffect } from 'vue'
const searchParams = reactive({
keyword: '',
page: 1,
size: 10
})
const list = ref([])
const total = ref(0)
watchEffect(async (onInvalidate) => {
const { keyword, page, size } = searchParams // 自动追踪这三个字段
const prev = { aborted: false }
onInvalidate(() => (prev.aborted = true))
try {
const res = await fetch(
`/api/search?k=${keyword}&p=${page}&s=${size}`
)
if (!prev.aborted) {
const data = await res.json()
list.value = data.items
total.value = data.total
}
} catch (e) {
if (!prev.aborted) console.warn(e)
}
})
? 不用展开写 [searchParams.keyword, searchParams.page, ...],解构即追踪
进阶技巧:防抖 + 跳过初始空值
比如搜索框输入后延迟请求,且不希望 keyword = '' 时发空请求:
import { ref, watchEffect } from 'vue'
import { useDebounceFn } from '@vueuse/core'
const keyword = ref('')
const results = ref([])
// 防抖函数需在 setup 外或用 onBeforeUnmount 清理,此处简化为手动控制
let pending = 0
watchEffect((onInvalidate) => {
const id = ++pending
onInvalidate(() => {
if (id === pending) pending = 0
})
if (!keyword.value.trim()) {
results.value = []
return
}
const timer = setTimeout(async () => {
if (id !== pending) return // 已被取消
try {
const res = await fetch(`/api/search?q=${keyword.value}`)
results.value = await res.json()
} catch (e) {
if (id === pending) console.error(e)
}
}, 300)
})
⚠️ 注意:watchEffect 本身不提供防抖,需结合 setTimeout 或 useDebounceFn 手动实现
不复杂但容易忽略:只要代码里用了响应式变量,watchEffect 就会盯住它;重点是让请求逻辑和参数读取写在同一个作用域里,自然建立响应联系。











