响应式数据本身不会泄露,真正问题是手动创建的外部引用未随组件销毁:全局事件需用具名函数配对绑定/解绑;定时器和异步请求须清理;第三方实例要调用销毁方法;大数据量应避免深度响应式,改用 shallowRef 或 markRaw。

响应式数据本身不会“泄露”,真正的问题是:你手动创建的外部引用,没有随组件一起销毁。Vue 能自动清理它自己建立的响应式依赖和 effect,但对定时器、全局事件、第三方实例这些“借来的资源”不负责。
全局事件监听未解绑
直接在 onMounted 里用 window.addEventListener 却没配对移除,是最常见的泄漏点。匿名函数会导致 removeEventListener 失效,必须使用同一函数引用。
- 错误写法:
window.addEventListener('scroll', () => {...}) - 正确写法:先声明具名函数,
onUnmounted中用相同引用移除 - 推荐封装成组合式函数,如
useEventListener(window, 'resize', handler),内部自动绑定/解绑
定时器与异步请求未取消
组件卸载后,setTimeout 或未完成的 fetch 仍可能执行,并尝试修改已销毁的 ref 或调用已不存在的方法。
- 定时器:保存返回值(如
timerId),在onBeforeUnmount中调用clearTimeout或clearInterval - 异步请求:优先用
AbortController,signal传入fetch;axios 可配合CancelToken或封装可取消的请求函数 - 避免在回调中直接访问
ref.value,加一层if (isMounted)判断更稳妥
第三方库实例未销毁
ECharts、Mapbox、D3 等库常在 DOM 上挂载状态或监听器。若只初始化不销毁,实例会持续持有对 DOM 和 Vue 组件的引用。
立即学习“前端免费学习笔记(深入)”;
- 务必在
onBeforeUnmount中调用对应销毁方法,如chart.dispose()、map.remove() - 避免在
mounted或onMounted中直接 new 实例后无后续管理 - 若使用
ref持有实例,确保该 ref 不被其他长期存活对象意外捕获
大数据量响应式劫持
把几万条原始数据直接塞进 ref 或 reactive,会触发深度 Proxy 代理,不仅初始化慢,还大幅增加内存占用和 GC 压力。
- 用
shallowRef替代ref,仅对外层做响应式 - 对原始数据调用
markRaw,彻底跳过响应式转换 - 分页加载、虚拟滚动、懒渲染等策略比“全量响应式”更治本


















