页面卸载时未清理定时器、事件监听器、网络请求及dom引用,会导致主线程抢占、内存泄漏和后续跳转卡顿;需在组件卸载时显式清除setinterval/settimeout、removeeventlistener、abort fetch/关闭websocket,并将dom引用置为null。

页面卸载时定时器没清理导致后续跳转卡顿
很多卡顿发生在从 A 页面跳到 B 页面后,B 页面刚加载就明显掉帧或响应延迟——根源常是 A 页面残留的 setInterval 或 setTimeout 仍在后台运行,持续抢占主线程。这类定时器不会因页面跳转自动销毁,尤其在 SPA 中用 router.push() 或 history.pushState() 时更隐蔽。
- 检查所有组件
mounted/useEffect中启动的定时器,是否在beforeUnmount/useEffect清理函数里调用了clearInterval或clearTimeout - 避免直接传匿名函数给
setInterval:它无法被引用清除;改用命名函数或存为变量:const timer = setInterval(...);,再在卸载时clearInterval(timer) - 注意第三方库(如轮播图、统计 SDK)可能内部启用了定时器但未暴露清理接口,需查阅文档或手动 patch
事件监听器未解绑造成内存泄漏和滚动卡顿
页面跳转后,如果旧 DOM 节点上的 addEventListener 没被 removeEventListener 移除,不仅占用内存,还会在新页面触发无关回调——比如在新页滚动时,旧页绑定的 scroll 监听器仍执行 document.querySelector,直接拖慢帧率。
生成Claude风格的精美单页HTML汇报文件。当用户需要生成"汇报"、"周报"、"月报"、"项目进度"、"复盘"、"演示"、"slide deck"、"状态报告"、"工作总结"时触发。支持6种模板:周报(weekly)、项目进度(project)、月度总结(monthly)、复盘报告(postmortem)、演示文稿(slid
- 监听器必须与添加时使用**完全相同的函数引用**才能正确移除;不要用箭头函数或内联函数,改用外部声明的函数名
- 优先使用
once: true选项,适用于只执行一次的逻辑,省去手动解绑 - 对全局事件(
window、document)尤其要警惕;SPA 中常见错误是在路由守卫里加了window.addEventListener('resize', ...)却忘了在离开时 remove
WebSocket 或 Fetch 请求未 abort 导致连接堆积
页面卸载前未调用 AbortController.abort(),会导致请求继续挂起、连接保持打开状态,后续页面的网络请求可能被阻塞(尤其在连接数有限的浏览器中),同时残留的 onmessage 回调仍会执行。
- 每个
fetch都应配一个AbortController实例,并在组件卸载时调用controller.abort() - WebSocket 实例需显式调用
ws.close(),且监听onclose确保不重复关闭;避免仅靠ws.onmessage = null以为已断开 - 检查 DevTools 的 Network 面板,在跳转后是否仍有 pending 状态的请求;若有,说明资源释放路径没走通
DOM 引用未置空引发闭包驻留
一个容易被忽略的点:JS 中保存了对旧页面 DOM 元素的引用(比如缓存了 document.getElementById('chart')),即使页面已跳走,该 DOM 节点仍无法被 GC 回收,连带其绑定的事件、数据、甚至整个组件实例都被钉在内存里——表现为内存占用缓慢上涨,多次跳转后页面越来越卡。
- 在卸载逻辑中,主动将缓存的 DOM 变量设为
null:cachedEl = null; - 避免在闭包中长期持有 DOM 引用;如需复用,改用 ID 字符串代替节点本身,需要时再
querySelector - 用 Chrome DevTools 的 Memory 面板拍两次堆快照(跳转前后),对比 “Detached DOM tree” 大小;持续增长就是典型信号
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










