单页应用路由切换时video元素必须执行四步清理:pause()、清空src、调用load()、移除事件监听器,否则导致内存持续增长;需复用同一video节点并配合框架卸载钩子执行,且须用DevTools验证释放效果。

单页应用(SPA)中路由切换时,video 元素若未主动清理,极易造成内存持续增长——解码器不释放、GPU纹理堆积、缓冲区残留,甚至出现白屏或卡死。这不是浏览器缺陷,而是资源管理缺失的必然结果。关键不是“删掉 DOM”,而是让视频彻底“断电休眠”。
必须执行的四步清理动作
仅靠 v-if 销毁组件或 remove() 移除元素远远不够。每次路由离开前,需同步完成以下操作:
-
调用
video.pause():停止播放和解码行为,防止后台持续消耗 CPU -
设
video.src = ""(或removeAttribute('src')):清空源地址,中断预加载与网络连接 -
调用
video.load():触发emptied事件,重置内部状态,清空缓冲区与解码上下文 -
移除所有事件监听器:如
timeupdate、ended、error,尤其避免匿名函数导致无法解绑
优先复用 video 元素,禁止频繁创建销毁
反复 document.createElement('video') 或模板中用 v-for 渲染多个 video 标签,是内存暴增的主因。每个实例都独占解码器资源,而 JS 引用未清干净时,GC 无法回收。
- 固定使用一个
<video>节点,路由切换时只更新其src,并严格配套执行上述四步清理 - 若涉及 Blob URL(如
URL.createObjectURL(blob)),清理时必须配对调用URL.revokeObjectURL(video.src) - 若使用
srcObject(如摄像头流),需手动停止轨道:video.srcObject?.getTracks().forEach(t => t.stop()),再设video.srcObject = null
与框架生命周期深度绑定
不能依赖 props 变化或 DOM 隐藏来“自动”释放。必须在框架确认组件真正卸载的时机执行清理:
立即学习“前端免费学习笔记(深入)”;
-
Vue:在
onBeforeUnmount中完整执行四步;避免用v-show,改用v-if确保 DOM 销毁,但前提是清理逻辑已写在卸载钩子内 -
React:用
useEffect的 cleanup 函数(推荐useLayoutEffect初始化),确保 DOM 插入后立即绑定,卸载前彻底解绑 -
禁止模板直写
<video :src="url">:这会让浏览器在渲染阶段就自动加载,失去控制权。应初始化时设空src,挂载后再赋值
验证是否真正释放
代码写对了不等于内存真释放。必须通过工具确认:
- Chrome DevTools → Memory → “Take Heap Snapshot”,筛选
HTMLVideoElement,对比切换前后数量是否回落 - 打开 chrome://media-internals,查找对应视频项,确认
pipeline_state变为STOPPED或ENDED,而非PLAYING或WAITING_FOR_DECODER - Performance 面板录制完整切换流程,观察内存曲线是否阶梯上升后回落,而非单边上涨



















