组合式函数必须显式清理外部资源,否则易致内存泄漏;应统一用effectScope批量管理副作用,或手动保存watch/watchEffect返回的stop函数并在onUnmounted中调用,同时及时解绑事件、销毁第三方实例、清除定时器。

Vue 3 中组合式函数(Composable)本身不自动管理外部资源,一旦封装了事件监听、定时器、异步请求或第三方实例,就容易在组件卸载后继续持有引用,造成内存泄漏和副作用残留。关键不是“写不写”,而是“清不清”——所有手动创建的响应式副作用,都必须有明确的释放路径。
统一用 effectScope 收敛副作用
把 watch、computed、事件绑定、定时器等全部纳入一个 effectScope,组件卸载时调用 scope.stop() 即可批量清理:
- 避免零散管理多个 stop 函数,逻辑更集中
- effectScope 自动跟踪内部创建的所有响应式依赖(包括嵌套的 watchEffect)
- 配合 onUnmounted 使用,确保组件销毁时必执行
- 适用于自定义 Hook 内部所有需生命周期绑定的操作
手动 stop 所有 watch 和 watchEffect
watch / watchEffect 返回的 stop 函数不能只靠 Vue 自动清理——尤其当监听源来自全局状态、跨组件共享 ref 或在非 setup 上下文中调用时:
- 显式保存返回值:const stop = watch(data, handler)
- 在 onUnmounted 中调用 stop(),不依赖自动卸载机制
- watchEffect 可利用 onCleanup 处理异步中间态(如取消未完成的 fetch 请求)
- 避免在回调外直接调用 stop(),否则监听器尚未建立就终止
及时解绑原生事件与销毁第三方实例
组合式函数若操作 DOM 或集成第三方库,必须主动释放资源:
立即学习“前端免费学习笔记(深入)”;
- 给 window/document 添加的事件监听,必须配对 removeEventListener
- echarts.init()、IntersectionObserver、ResizeObserver 等,调用对应 .dispose() 或 .disconnect()
- WebSocket 实例要关闭连接、清空 onmessage/onerror 回调引用
- setTimeout/setInterval 要用 clearTimeout/clearInterval 清除 ID
警惕闭包捕获响应式对象
异步回调中直接访问 ref 或 reactive 数据,会形成隐式强引用,阻止 GC 回收组件实例:
- 组件已卸载,但定时器回调仍持有 count.value 引用 → 实例无法被回收
- 解决方式:在 onUnmounted 中清除定时器;或在异步开始前检查组件是否活跃(例如用 isRefAlive 辅助判断)
- 避免在 setup 外部或工具函数中长期持有组件内 ref,必要时用 toRaw 或 unref 解耦


















