HTML5音视频元素需与组件生命周期严格对齐:React用useLayoutEffect+useRef确保DOM插入后初始化并清理;Vue用onMounted/onBeforeUnmount配合v-if控制DOM存在性;均需手动管理加载、播放、事件监听与资源释放。

HTML5多媒体(<audio> 和 <video>)在 React 或 Vue 中不是普通组件,其生命周期与框架的渲染周期不完全同步——直接用 ref 操作 DOM 元素时,容易遇到元素未挂载、状态不同步、内存泄漏或重复初始化等问题。关键在于:把媒体元素的创建、加载、播放控制、事件监听和销毁,与组件的挂载、更新、卸载严格对齐。
React 中 useLayoutEffect + useRef 精确控制挂载时机
媒体元素需在 DOM 真实插入后立即初始化(如设置 src、预加载、监听 canplay),而 useEffect 在浏览器绘制后执行,可能错过早期事件。useLayoutEffect 在 DOM 更新后、绘制前同步运行,更适合:
- 用
useRef保存媒体元素引用,避免每次渲染重建实例 - 在
useLayoutEffect中检查 ref 是否存在,再调用load()或设置preload="auto" - 在 cleanup 函数中调用
mediaEl.pause()、mediaEl.src = ""并移除所有事件监听器(如timeupdate、ended) - 若依赖 props(如
src)变化,应在 effect 的依赖数组中包含它,并在 src 变更时重置状态(pause()+load())
Vue 3 中 onMounted / onBeforeUnmount + v-if 控制 DOM 存在性
Vue 的组合式 API 提供了明确的钩子,但要注意:<video> 在 v-if="false" 切换时会彻底销毁 DOM,而 v-show 仅切换 display,媒体状态(如播放位置、缓冲)会被保留。实际使用建议:
- 在
onMounted中通过ref获取元素,调用load(),绑定事件(推荐用addEventListener而非内联@timeupdate,便于统一移除) - 在
onBeforeUnmount中暂停、清空src、移除监听器;若组件可能复用(如路由缓存),还需在onActivated中恢复播放状态 - 避免在模板中直接写
<video :src="src">——这会触发浏览器自动加载,失去控制权;应先设src="",待 mounted 后再赋值
跨框架通用:用自定义 Hook / Composable 封装媒体状态机
播放、暂停、静音、时间跳转、加载状态等逻辑高度重复,适合抽离为可复用逻辑单元:
立即学习“前端免费学习笔记(深入)”;
- React:封装
useVideoPlayer(ref),返回{ isPlaying, currentTime, volume, play, pause, seek },内部管理timeupdate节流、waiting/canplay状态转换 - Vue:编写
useAudioPlayer()composable,用ref管理播放状态,配合watch响应src变化,自动处理加载流程 - 统一处理错误:监听
error事件,触发重试逻辑或上报,避免媒体元素卡死在networkState === 0
注意资源释放:隐藏 ≠ 停止,卸载 ≠ 清理
常见疏漏点:
- 组件
display: none或v-show=false时,<video>仍在解码和播放,消耗 CPU 和网络带宽 - SPA 路由跳转时,若未手动
pause(),音频可能继续播放,且监听器持续触发(造成内存泄漏) - 多个相同 media ref 被多个组件共享时(如全局播放器),需用引用计数或事件总线协调状态,避免冲突
- Safari 对后台标签页的
autoplay限制极严,即使设置了muted,也建议首次交互(如点击按钮)后再调用play()


















