✨ feat(im): 初始化消息转发 v0.4:第四次评审(语音播放器资源释放打磨)
agent 三轮复审后的质量打磨,无功能变更。
- useVoicePlayer.stop:audio.removeAttribute('src') + audio.load() 替代 audio.src = '';不会触发空 src 加载的 error 事件,也能让浏览器立即释放底层 decoder buffer
- useVoicePlayer.play:同 key 再点的 stop() 改 stop(key),意图自解释(我想停的就是我自己)
- useVoicePlayer 移除未消费的 currentKey 暴露;调用方都走 isPlaying(key) 派生
- home/index.vue onUnmounted 追加 voicePlayer.stop():模块级单例 audio 不会随视图卸载自动停,补主壳兜底
im
parent
0b07091e79
commit
cc93b8a742
|
|
@ -1,4 +1,4 @@
|
|||
import { computed, ref } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
/**
|
||||
* 语音播放全局互斥
|
||||
|
|
@ -37,7 +37,10 @@ function stop(key?: VoiceKey) {
|
|||
return
|
||||
}
|
||||
task.audio.pause()
|
||||
task.audio.src = ''
|
||||
// removeAttribute('src') + load() 是 W3C 推荐的释放姿势:不会触发空 src 加载导致的 error 事件,
|
||||
// 也能让浏览器立即释放底层 decoder buffer,比 audio.src = '' 更干净
|
||||
task.audio.removeAttribute('src')
|
||||
task.audio.load()
|
||||
currentTask.value = null
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +55,7 @@ function play(key: VoiceKey, url: string) {
|
|||
return
|
||||
}
|
||||
if (currentTask.value?.key === key) {
|
||||
stop()
|
||||
stop(key)
|
||||
return
|
||||
}
|
||||
stop()
|
||||
|
|
@ -71,11 +74,9 @@ function play(key: VoiceKey, url: string) {
|
|||
}
|
||||
|
||||
export function useVoicePlayer() {
|
||||
/** 当前播放的 key;给气泡 / 调试用 */
|
||||
const currentKey = computed(() => currentTask.value?.key ?? null)
|
||||
/** 指定 key 是否正在播放 */
|
||||
function isPlaying(key: VoiceKey): boolean {
|
||||
return currentTask.value?.key === key
|
||||
}
|
||||
return { currentKey, isPlaying, play, stop }
|
||||
return { isPlaying, play, stop }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import { useDraftStore } from './store/draftStore'
|
|||
import { useFaceStore } from './store/faceStore'
|
||||
import { useMessagePuller } from './composables/useMessagePuller'
|
||||
import { useMessageSender } from './composables/useMessageSender'
|
||||
import { useVoicePlayer } from './composables/useVoicePlayer'
|
||||
import { ImConversationType } from '../utils/constants'
|
||||
import { StorageKeys } from '../utils/storage'
|
||||
import type { Conversation } from './types'
|
||||
|
|
@ -56,6 +57,7 @@ const draftStore = useDraftStore()
|
|||
const faceStore = useFaceStore()
|
||||
const { pullOnce } = useMessagePuller()
|
||||
const { readActive, syncPrivateReadStatus } = useMessageSender()
|
||||
const voicePlayer = useVoicePlayer()
|
||||
|
||||
/** 初始化:先吃本地缓存让首屏立即渲染,再远端刷新最新数据,最后建实时通信拉离线消息 */
|
||||
onMounted(async () => {
|
||||
|
|
@ -135,11 +137,13 @@ function onBeforeUnload() {
|
|||
}
|
||||
window.addEventListener('beforeunload', onBeforeUnload)
|
||||
|
||||
/** 离开 IM 主壳:主动断 WebSocket(disconnect 内部已清掉 onclose 防自动重连)+ flush 草稿 + 表情缓存 reset + 解绑 unload */
|
||||
/** 离开 IM 主壳:主动断 WebSocket(disconnect 内部已清掉 onclose 防自动重连)+ flush 草稿 + 表情缓存 reset + 解绑 unload + 停语音 */
|
||||
onUnmounted(() => {
|
||||
webSocketStore.disconnect()
|
||||
draftStore.flushPersist()
|
||||
faceStore.reset()
|
||||
// 模块级单例 audio 不会随视图卸载自动停,主动停掉避免切路由后语音继续响
|
||||
voicePlayer.stop()
|
||||
window.removeEventListener('beforeunload', onBeforeUnload)
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue