音视频播放进度应封装为含 currentTime、duration、isSeeking 的响应式对象,通过监听 timeupdate 事件更新,配合防抖、批量提交和跨窗口通信机制实现精准、高效、一致的状态管理。

音视频播放进度这类数值状态,核心在于可读、可写、可响应、可持久化。Pinia 的 state 天然适合承载它,但直接存一个数字远远不够——你需要考虑精度、同步时机、暂停恢复逻辑,以及跨组件甚至跨窗口的一致性。
进度值必须是响应式且带单位语义
不要只存 currentTime: 123.45 这样的裸数字。建议用对象封装,明确含义和单位:
-
currentTime: number—— 当前毫秒级时间戳(与<audio>/<video></video></audio>的currentTime对齐) -
duration: number | null—— 总时长(加载后才可知,初始为null) -
isSeeking: boolean—— 标识是否正在拖拽(避免拖拽中频繁触发自动更新)
这样定义更清晰:
state: () => ({
currentTime: 0,
duration: null,
isSeeking: false
})
更新进度不能依赖定时器硬轮询
很多新手会用 setInterval 每 100ms 更新一次 currentTime,这既不准又浪费资源。正确做法是监听原生媒体事件:
- 在
actions中绑定timeupdate事件,仅当播放中才写入this.currentTime = video.currentTime - 手动拖拽时,先设
isSeeking = true,拖完再设false,并在watch或onBeforeUnmount中清理事件 - 暂停/播放切换时,不重置
currentTime,只控制播放状态
进度需要防抖 + 节流 + 延迟写入
频繁更新状态会导致多余渲染或本地存储压力。尤其在迷你播放器、目录页、笔记面板多个地方同时监听时:
- 对
currentTime的变更做debounce(500):用户停止操作半秒后再提交到 store - 使用
$patch批量更新,比如同时记录进度 + 最后活跃时间:this.$patch({ currentTime, lastActiveAt: Date.now() }) - 若需持久化(如关掉页面再打开继续播),在
actions中调用localStorage.setItem或indexedDB,但只在isSeeking === false && isPlaying时写入
跨窗口/跨标签页需额外同步机制
Electron 多窗口或浏览器多标签下,Pinia 实例彼此隔离。仅靠 store 无法共享进度:
- 主窗口更新了
currentTime,歌词窗口不会自动感知 - 解决方案不是“让 Pinia 同步”,而是用底层通信:Electron 中用
ipcRenderer.send('progress-update', time)+ipcMain.on广播;浏览器中可用BroadcastChannelAPI - Pinia store 只负责本进程内的状态维护,同步逻辑应放在
actions内部封装,例如:syncProgressToOtherWindows(time)


















