音频组件expose事件定义
概述
本节通过 defineExpose 暴露音频组件的方法供父组件调用,实现组件与外层播放列表的交互,同时优化实例销毁逻辑以支持多实例场景。
组件生命周期优化
问题:全局 unload 影响多实例
// 问题代码:unload 会销毁页面上所有的 Howl 实例
onUnmounted(() => {
Howler.unload() // ❌ 影响其他音频组件实例
})
// 优化:只销毁当前实例
onUnmounted(() => {
if (audioInstance.value) {
audioInstance.value.unload() // ✅ 只销毁自身
audioInstance.value = null
}
})
typescript
兼容用户自定义回调
// 兼容用户通过 options 传入的事件回调
onBeforeMount(() => {
initAudio(props.src, {
onload() {
// 用户自定义 onload
if (props.options?.onload) {
props.options.onload()
}
// 默认 onload 逻辑
state.duration = audioInstance.value!.duration()
},
onplay(id) {
if (props.options?.onplay) {
props.options.onplay(id)
}
state.playing = true
}
})
})
typescript
defineExpose 暴露方法
类型定义
// types.ts
export interface AudioPlayerMethods {
play: () => void
pause: () => void
stop: () => void
seekTo: (seconds: number) => void
stepForward: () => void
stepBackward: () => void
setVolume: (volume: number) => void
setRate: (rate: number) => void
setLoop: (loop: boolean) => void
getState: () => AudioPlayerState
}
typescript
组件中实现
// AudioPlayer.vue
const controls = useAudioControls(audioInstance, state)
defineExpose<AudioPlayerMethods>({
play: controls.play,
pause: controls.pause,
stop: controls.stop,
seekTo: controls.seekTo,
stepForward: controls.stepForward,
stepBackward: controls.stepBackward,
setVolume: controls.setVolume,
setRate: controls.setRate,
setLoop(loop: boolean) {
audioInstance.value?.loop(loop)
},
getState: () => ({ ...state })
})
typescript
父组件调用
<template>
<AudioPlayer
ref="playerRef"
:src="currentTrack.src"
:list="playlist"
@ended="handleTrackEnd"
/>
<div class="playlist-controls">
<el-button @click="playerRef?.play()">外部播放</el-button>
<el-button @click="playerRef?.pause()">外部暂停</el-button>
<el-button @click="playerRef?.seekTo(30)">跳到 30 秒</el-button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import AudioPlayer from './AudioPlayer.vue'
import type { AudioPlayerMethods } from './types'
const playerRef = ref<AudioPlayerMethods>()
// 通过 ref 调用 expose 的方法
function handleTrackEnd() {
playerRef.value?.stop()
}
</script>
vue
expose vs emits 适用场景
| 方式 | 方向 | 适用场景 |
|---|---|---|
defineExpose | 父 → 子(命令式) | 控制播放、暂停、跳转等操作 |
defineEmits | 子 → 父(事件式) | 通知播放结束、进度变化等状态 |
父组件
├── playerRef.play() ← expose(父调用子方法)
├── playerRef.seekTo(30) ← expose
└── @ended="handleEnd" ← emit(子通知父事件)
text
小结
- 使用
defineExpose暴露控制方法,允许父组件命令式操控音频播放 - 多实例场景下使用
instance.unload()而非Howler.unload(),避免影响其他实例 - 兼容用户通过
options传入的onload/onplay等回调 expose用于父→子的命令调用,emit用于子→父的事件通知
↑