watchEffect 本身不负责挂载 DOM,但能配合 ref 和生命周期自动响应节点变化;通过 ref 声明引用并监听其存在,首次执行若节点就位即操作,否则待后续挂载时触发;推荐搭配 flush: 'post' 确保读取最新布局,并与 onMounted 协同保障首次可靠性和后续响应性,同时需用 onInvalidate 清理副作用避免内存泄漏。

watchEffect 本身不负责挂载 DOM,但它能配合生命周期和 ref,在 DOM 准备就绪后自动响应节点变化,特别适合需要“一有节点就操作、节点更新就重试”的场景。
用 ref 声明节点引用,watchEffect 监听其存在
这是最自然的结合方式:先在模板中用 ref="xxx" 绑定元素,setup 中用 ref(null) 声明引用,再让 watchEffect 检查 ref.value 是否已挂载。
- 不需要手动等 onMounted,watchEffect 在组件挂载后会立即执行一次,此时若节点已就位(多数情况成立),就能立刻拿到 DOM
- 如果首次执行时 ref.value 还是 null(比如异步内容、v-if 切换),watchEffect 会在后续 ref.value 变成真实 DOM 节点时再次触发
- 写法简洁,逻辑自包含,避免重复判断
示例:
<script setup><br>import { ref, watchEffect } from 'vue'<br><br>const targetEl = ref(null)<br><br>watchEffect(() => {<br> if (targetEl.value) {<br> targetEl.value.style.color = 'blue'<br> console.log('DOM 已就位,宽:', targetEl.value.offsetWidth)<br> }<br>})<br></script>
配合 flush: 'post' 确保读取更新后的 DOM
当节点内容或样式由响应式数据驱动(比如 v-if/v-for 更新、class 绑定变化)时,watchEffect 默认在 DOM 更新后执行,但需显式指定 flush: 'post' 才能稳定读取最新布局信息。
立即学习“前端免费学习笔记(深入)”;
Element Plus Vue3 桌面组件库。在开发 Vue 3.x 页面时调用,使用 Element Plus 组件(如 el-button、el-input、el-form、el-table 等)。
- 默认 flush 就是 'post',可省略;但显式写出更清晰,尤其涉及 offsetWidth/offsetHeight/clientRect 等布局测量时
- 若在 DOM 更新前操作(如用 pre),可能拿到旧尺寸或 null,一般不推荐用于节点获取
示例(带数据驱动更新):
const show = ref(true)const targetEl = ref(null)
watchEffect(() => {
if (targetEl.value && show.value) {
console.log('当前宽度:', targetEl.value.offsetWidth)
}
}, { flush: 'post' })
与 onMounted 协同,兼顾首次可靠性和后续响应性
watchEffect 不保证首次执行时节点一定存在(尤其含 v-if 或动态组件),所以更稳妥的做法是:onMounted 做一次初始化检查,watchEffect 处理后续变动。
- onMounted 保证 DOM 挂载完成,适合做一次性设置(如 focus、初始动画)
- watchEffect 补充监听 ref.value 变化,覆盖节点被销毁又重建的情况(如 v-if 切换、列表项重渲染)
- 两者不冲突,可共存,分工明确
示例:
import { ref, onMounted, watchEffect } from 'vue'const inputEl = ref(null)
onMounted(() => {
if (inputEl.value) inputEl.value.focus()
)
watchEffect(() => {
if (inputEl.value) {
inputEl.value.addEventListener('input', handleInput)
return () => inputEl.value?.removeEventListener('input', handleInput)
}
})
注意清除副作用,避免内存泄漏
当 watchEffect 内部注册了事件监听器、定时器或 ResizeObserver 等资源,必须用 onInvalidate 清理上一次的旧实例。
- 每次新执行前,Vue 会自动调用上一轮传入的 onInvalidate 回调,确保旧监听被移除
- 这对 ref 节点频繁切换(如列表滚动、模态框开关)尤为重要
- 不清理会导致重复绑定、事件堆积、DOM 无法释放等问题
示例(监听元素尺寸变化):
import { ref, watchEffect } from 'vue'const box = ref(null)
let resizeObserver
watchEffect((onInvalidate) => {
if (!box.value) return
resizeObserver = new ResizeObserver(entries => {
console.log('尺寸变了:', entries[0].contentRect)
})
resizeObserver.observe(box.value)
onInvalidate(() => {
resizeObserver?.unobserve(box.value)
resizeObserver?.disconnect()
})
})

















