vue 渲染器通过 vnode 卸载流程提供清理时机,由钩子、指令或 effectscope 等协同完成:先递归卸载子 vnode,再执行 beforeunmount/unmounted 钩子,最后移除 dom;onunmounted 适合手动清理定时器、事件监听等;effectscope 可自动管理副作用;watch/watcheffect 内置 oncleanup 支持细粒度清理。

Vue 渲染器本身不直接执行副作用清理,而是通过精准控制 vnode 的卸载流程,为清理逻辑提供执行时机和上下文。真正的清理工作由开发者编写的钩子、指令或框架内部机制协同完成。
卸载流程触发清理入口
当组件从 DOM 树中移除(如 v-if=false、路由切换、renderer.unmount()),渲染器会调用 unmountElement(内部函数)递归处理 vnode。这个过程分三步展开:
- 先递归卸载所有子 vnode,确保嵌套组件、动态内容、过渡节点等全部退出
- 再调用指令的 beforeUnmount 和 unmounted 钩子,让自定义指令(如拖拽、焦点管理)有机会清理事件监听、定时器、ResizeObserver 等资源
- 最后执行平台层移除操作(hostRemove(el)),并清空 vnode.el 和 vnode.component 引用
onUnmounted 是最常用的清理位置
这是组合式 API 中专为卸载清理设计的生命周期钩子,在 vnode 从树中移出、DOM 尚未被移除时执行。它天然适配父子组件卸载顺序(子先于父),适合集中释放资源:
- 清除定时器:clearInterval / clearTimeout,避免回调在已销毁组件中执行
- 移除全局事件监听:window.removeEventListener、document.removeEventListener
- 取消未完成的请求(配合 Axios CancelToken 或 fetch AbortController)
- 从共享状态中注销当前实例(如 shadcn-vue 中的 allItems.value.delete(id))
effectScope 提供更健壮的自动清理能力
当逻辑抽离到可复用函数(如 usePolling、useWebSocket)时,手动管理 onUnmounted 容易遗漏。此时 effectScope 更可靠:
- 用 scope.run(() => { ... }) 包裹所有响应式操作(watch、computed、setTimeout、addEventListener)
- 所有注册的副作用会被自动收集进该作用域
- 调用 scope.stop() 即可一键停止并清理全部关联资源,无需逐个 stop 或 remove
- 可与 onUnmounted 结合:在钩子里调用 scope.stop(),实现跨组件边界的统一生命周期管理
watch/watchEffect 内置清理支持
对于侦听类副作用,Vue 提供了更细粒度的清理机制:
- watchEffect 回调中可调用 onCleanup(fn),该函数会在下一次 effect 执行前或 effect 停止时自动触发(例如取消上一轮未完成的 API 请求)
- watch 的回调同样支持 onCleanup,适合处理依赖变化时需中断前序异步任务的场景
- 也可显式调用 watch 返回的 stop() 函数,适用于需要提前终止监听的逻辑分支
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










