全局状态生命周期必须由创建者显式管理:模块初始化时注册,卸载前主动清理;需统一注册与自动清理机制,禁用隐式全局变量和意外闭包捕获,并用devtools验证销毁效果。

明确全局状态的生命周期边界
大型单页应用中,全局状态常通过 store 实例(如 Redux/Vuex/Pinia)、事件总线、定时器引用、全局监听器或挂载在 window 上的对象 暴露。关键不是“要不要用”,而是“谁创建、谁负责销毁”。规范要求:每个模块/页面/组件在初始化时显式注册其依赖的全局资源,并在卸载前主动清理——不能依赖 GC 被动回收,尤其当存在闭包引用或 DOM 交叉引用时。
统一注册与自动清理机制
在路由切换或组件卸载前,触发标准化的销毁钩子:
- 使用 路由守卫(如 Vue Router 的
beforeRouteLeave或 React Router 的useEffect cleanup) 触发当前模块关联的全局状态释放逻辑 - 为 store 模块设计
unmount()方法,集中注销订阅、清空缓存、取消 pending 请求(如 Axios CancelToken) - 对 window 级变量,约定命名空间前缀(如
__myapp_cache_),并在模块卸载时delete window.__myapp_cache_xxx - 避免直接使用
setInterval,改用可取消的封装(如const timer = setInterval(...); return () => clearInterval(timer);)
禁止隐式全局变量和意外闭包捕获
内存泄漏常源于无意识的引用保留:
- 禁用未声明变量赋值(
foo = {}→ 自动挂到window),开启 ESLintno-implicit-globals和no-unused-vars - 组件内定义的回调函数若被外部对象(如第三方 SDK、事件总线)长期持有,需确保该回调不捕获组件实例或局部大对象;必要时用
useCallback+ 依赖数组控制重建,或用弱引用包装(WeakRef配合手动释放) - 监听 DOM 事件时,始终使用
addEventListener+ 对应removeEventListener,且 handler 引用必须一致(避免匿名函数)
用 DevTools 定期验证销毁效果
仅靠代码规范不够,需实证:
- 在 Chrome DevTools 的 Memory 面板 中录制 Heap Snapshot,切换页面后对比前后差异,重点关注
Closure、Detached DOM tree和重复出现的模块名 - 使用 Performance 面板录制用户操作流,勾选 “Memory” 采集,观察 JS heap size 是否随页面跳转持续增长
- 对高频模块,编写轻量级销毁断言测试:例如进入页面后检查
window.__myapp_state_moduleA存在,离开后为undefined,并触发 GC 后再次快照确认对象已不可达
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











