Vue 3 中响应式引用未销毁会导致内存泄漏,需在 onBeforeUnmount 中清理定时器、事件监听器、watch 停止函数及未完成 Promise,并避免闭包意外保留响应式对象引用。

响应式引用未销毁是内存泄漏的常见原因
在 Vue 3 的 Composition API 中,使用 ref、reactive 或 computed 创建的响应式对象,如果在组件卸载后仍被外部变量、定时器、事件监听器或闭包持有,就可能阻止垃圾回收,造成内存泄漏。尤其在异步操作(如 API 请求、watch 监听、setTimeout)中容易忽略清理逻辑。
用 onBeforeUnmount 主动清理副作用
在 setup 中注册需要手动释放的资源,必须配合生命周期钩子 onBeforeUnmount(而非 onUnmounted),确保在组件 DOM 卸载前、响应式系统停止追踪前完成清理:
- 清除未完成的 Promise(如 abort fetch 请求)
- 清除 setTimeout / setInterval 句柄
- 移除全局事件监听器(如 window.resize)
- 调用 watch 的停止函数(watch 返回值为一个 stop 函数)
示例:错误写法 —— 忘记 stop watch:
const stop = watch(source, callback);但未在 onBeforeUnmount 中调用 stop()
正确写法:
onBeforeUnmount(() => { stop(); })
避免闭包意外保留对响应式对象的引用
当把 ref 或 reactive 对象传入异步回调、第三方库或工具函数时,容易因闭包捕获而延长其生命周期。关键原则是:
- 尽量只传递原始值(如 ref.value)而非 ref 本身,除非明确需要响应性
- 在异步回调中访问 ref 前,先检查组件是否还存活(可用 getCurrentInstance()?.isMounted 或自定义标记)
- 避免将 ref 存入全局对象、缓存 Map 或未清理的 WeakMap
谨慎使用 toRef / toRefs 和 computed 缓存
toRef 和 toRefs 会创建对源 reactive 对象属性的“响应式引用”,它们不拥有数据所有权,因此不会自动随组件销毁而释放——但只要源对象被回收,它们也就失效。真正风险在于:computed 的缓存值若依赖外部可变状态(如未清理的 watchEffect),可能导致计算函数持续运行。
建议:
- 对一次性计算,优先用普通函数代替 computed
- 若需响应式计算且含副作用(如发起请求),务必在 onBeforeUnmount 中显式 stop 对应的 watchEffect
- 避免在 computed 中直接调用会产生副作用的操作(如 localStorage 写入、发送事件)

















