✨ 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
|
return
|
||||||
}
|
}
|
||||||
task.audio.pause()
|
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
|
currentTask.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +55,7 @@ function play(key: VoiceKey, url: string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (currentTask.value?.key === key) {
|
if (currentTask.value?.key === key) {
|
||||||
stop()
|
stop(key)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stop()
|
stop()
|
||||||
|
|
@ -71,11 +74,9 @@ function play(key: VoiceKey, url: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useVoicePlayer() {
|
export function useVoicePlayer() {
|
||||||
/** 当前播放的 key;给气泡 / 调试用 */
|
|
||||||
const currentKey = computed(() => currentTask.value?.key ?? null)
|
|
||||||
/** 指定 key 是否正在播放 */
|
/** 指定 key 是否正在播放 */
|
||||||
function isPlaying(key: VoiceKey): boolean {
|
function isPlaying(key: VoiceKey): boolean {
|
||||||
return currentTask.value?.key === key
|
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 { useFaceStore } from './store/faceStore'
|
||||||
import { useMessagePuller } from './composables/useMessagePuller'
|
import { useMessagePuller } from './composables/useMessagePuller'
|
||||||
import { useMessageSender } from './composables/useMessageSender'
|
import { useMessageSender } from './composables/useMessageSender'
|
||||||
|
import { useVoicePlayer } from './composables/useVoicePlayer'
|
||||||
import { ImConversationType } from '../utils/constants'
|
import { ImConversationType } from '../utils/constants'
|
||||||
import { StorageKeys } from '../utils/storage'
|
import { StorageKeys } from '../utils/storage'
|
||||||
import type { Conversation } from './types'
|
import type { Conversation } from './types'
|
||||||
|
|
@ -56,6 +57,7 @@ const draftStore = useDraftStore()
|
||||||
const faceStore = useFaceStore()
|
const faceStore = useFaceStore()
|
||||||
const { pullOnce } = useMessagePuller()
|
const { pullOnce } = useMessagePuller()
|
||||||
const { readActive, syncPrivateReadStatus } = useMessageSender()
|
const { readActive, syncPrivateReadStatus } = useMessageSender()
|
||||||
|
const voicePlayer = useVoicePlayer()
|
||||||
|
|
||||||
/** 初始化:先吃本地缓存让首屏立即渲染,再远端刷新最新数据,最后建实时通信拉离线消息 */
|
/** 初始化:先吃本地缓存让首屏立即渲染,再远端刷新最新数据,最后建实时通信拉离线消息 */
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
@ -135,11 +137,13 @@ function onBeforeUnload() {
|
||||||
}
|
}
|
||||||
window.addEventListener('beforeunload', onBeforeUnload)
|
window.addEventListener('beforeunload', onBeforeUnload)
|
||||||
|
|
||||||
/** 离开 IM 主壳:主动断 WebSocket(disconnect 内部已清掉 onclose 防自动重连)+ flush 草稿 + 表情缓存 reset + 解绑 unload */
|
/** 离开 IM 主壳:主动断 WebSocket(disconnect 内部已清掉 onclose 防自动重连)+ flush 草稿 + 表情缓存 reset + 解绑 unload + 停语音 */
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
webSocketStore.disconnect()
|
webSocketStore.disconnect()
|
||||||
draftStore.flushPersist()
|
draftStore.flushPersist()
|
||||||
faceStore.reset()
|
faceStore.reset()
|
||||||
|
// 模块级单例 audio 不会随视图卸载自动停,主动停掉避免切路由后语音继续响
|
||||||
|
voicePlayer.stop()
|
||||||
window.removeEventListener('beforeunload', onBeforeUnload)
|
window.removeEventListener('beforeunload', onBeforeUnload)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue