大屏路由切换后内存溢出根因是ECharts实例未dispose、VNode未回收、全局副作用未清理三者协同失效;须在onDeactivated中显式dispose图表、清除定时器与事件监听、暂停数据更新,onActivated中恢复,onUnmounted仅作兜底。

大屏应用路由切换后内存溢出,表面看是页面切多了卡死崩溃,根子往往不在“切得太快”,而在于ECharts 实例 + Vue 组件 VNode + 全局副作用三者没协同释放。尤其在 keep-alive 场景下,VNode 不销毁、图表不 dispose、定时器不清理,内存只会单向增长。
ECharts 实例未 dispose 是最直接的内存黑洞
每个 echarts.init(dom) 都会创建独立实例,绑定 DOM 引用、事件监听、resize 回调、动画帧、数据轮询定时器。若只设 chart = null 或靠 onUnmounted 清理,而 onUnmounted 在 keep-alive 下根本不会触发,这些资源就永远滞留。
- 必须在
onDeactivated中显式调用echarts.dispose() - 初始化前先查:
const existing = echarts.getInstanceByDom(dom); if (existing) existing.dispose(); - 多个图表要遍历清理,漏一个就可能让后续
init失败或复用异常
VNode 未回收的难点在于“看不见的引用链”
Vue3 的响应式系统会让组件内定义的 ref、computed、watch、onMounted 回调等持续持有对 DOM、数据、函数的引用。一旦这些引用被闭包、全局对象、第三方库(如 ECharts 的 setOption 回调)间接捕获,VNode 就无法被 GC 判定为“不可达”。
根据 package.json 中的 engines 字段自动检测 Node.js 版本需求,未安装 nvm 时自动安装并切换至对应版本。支持 Windows、macOS、Linux。适用于 Node.js 项目开发、版本切换和环境配置。
- 常见陷阱:在
setup中用箭头函数注册window.addEventListener('resize', handler),但没在onDeactivated解绑 → resize 事件持续触发,handler 里又引用了chart和data→ 整条链路锁死内存 - 更隐蔽的是
watch监听深层响应式对象,且回调中调用了异步接口,接口返回后又更新了另一个 ref → 循环引用虽不致命,但延长了 GC 回收时机
keep-alive 让问题更复杂:VNode 活着,逻辑却该停
<keep-alive> 缓存的是整个组件实例,包括 data、setup 返回的响应式状态、onActivated/onDeactivated 状态机。它不等于“页面还在用”,而是“随时可能再激活”。所以:
-
onMounted只负责初始化,不能承担清理职责 -
onUnmounted是兜底,但不一定执行,不能作为主清理入口 - 必须把“暂停业务逻辑”和“释放资源”拆到
onDeactivated:清 timer、解 event、设isPageActive = false、暂停数据拉取、chart.clear()或dispose() -
onActivated再恢复:重置开关、重连事件、chart.setOption()或重新 init
如何验证 VNode 是否真被回收?
不能只看 Chrome DevTools 的 “Memory” 标签页。要结合:
- Performance 录制中观察
Detached HTMLDivElement是否持续增长(说明 DOM 节点没释放) - Heap Snapshot 对比:筛选
VueComponent,看同一路由组件实例数是否随切换递增 - 搜索
echarts关键字,确认ECharts实例数量是否与页面切换次数一致(应始终 ≤ 当前活跃图表数)
本质上,这不是 Vue 或 ECharts 的 Bug,而是大屏场景下对“组件生命周期语义”的误读——把 keep-alive 当成普通缓存,忽略了它对内存管理提出的更高要求。


















