watchEffect 默认挂载即执行,因其在 setup 执行完毕、DOM 挂载前同步运行并自动求值,首次执行时收集响应式依赖,后续依赖变更时异步重执行。

watchEffect 默认就是组件挂载时立即执行一次,不需要额外配置。
为什么它能“挂载即执行”
watchEffect 的设计机制决定了它的首次执行时机:在组件 setup 执行完毕、DOM 尚未挂载完成前(接近 onBeforeMount 阶段)就会同步运行一次。这个过程自动完成,不依赖 mounted 钩子,也不需要设置 immediate: true 这类选项。
- 它会主动求值——一进入响应式追踪阶段就立刻执行传入的回调函数
- 执行时自动收集内部读取的所有响应式数据(如 ref、reactive 属性、route.params 等)作为依赖
- 后续只要这些依赖更新,它就会异步重新运行(微任务队列中)
常见误区与注意事项
虽然 watchEffect 天然支持“挂载即执行”,但实际使用中容易踩坑:
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
-
不能在首次执行时操作 DOM:此时 ref 绑定的元素还未挂载,
el.value是 null,需结合onMounted或nextTick -
不能在异步回调里调用 onInvalidate:清理函数必须在 watchEffect 回调的同步执行阶段注册,
await后调用无效 -
若函数内没读取任何响应式数据,它只执行一次且不再响应变化:比如只写
console.log('hello'),没有访问count.value或route.query等,那就退化成普通函数
典型用法示例
以下代码会在组件创建后立刻请求用户数据,并在路由 id 变化时自动刷新:
立即学习“前端免费学习笔记(深入)”;
import { watchEffect, ref } from 'vue'
import { useRoute } from 'vue-router'
setup() {
const route = useRoute()
const userInfo = ref(null)
const loading = ref(false)
watchEffect(async (onInvalidate) => {
loading.value = true
const id = route.params.id // 自动追踪依赖
const controller = new AbortController()
onInvalidate(() => controller.abort())
try {
const res = await fetch(`/api/users/${id}`, { signal: controller.signal })
userInfo.value = await res.json()
} catch (e) {
if (e.name !== 'AbortError') console.error(e)
} finally {
loading.value = false
}
})
return { userInfo, loading }
}

















