Vue3音频组件开发与使用指南
前言
在现代Web应用中,音频播放功能越来越常见,特别是在娱乐和社交应用中。本文将详细介绍如何在Vue3项目中开发一个功能完整的音频播放组件,包括播放控制、进度条、时间显示以及多音频实例管理等功能。
效果

项目结构
src/
├── components/
│ └── audio/
│ └── index.vue # 音频组件
├── store/
│ └── modules/
│ └── audio.ts # 音频状态管理
└── assets/
└── audio/
├── play.png # 播放按钮图标
└── pause.png # 暂停按钮图标
核心功能特性
- ✅ 播放/暂停控制
- ✅ 进度条拖拽调节
- ✅ 时间显示(当前时间/总时长)
- ✅ 播放速度调节
- ✅ 降噪功能
- ✅ 多音频实例管理
- ✅ 禁用状态支持
- ✅ 自定义样式
音频状态管理 (Pinia Store)
首先,我们使用Pinia来管理全局音频状态,确保同时只有一个音频在播放:
typescript
// src/store/modules/audio.ts
import { defineStore } from 'pinia';
type AudioElement = HTMLAudioElement & { uid?: symbol };
export const useAudioStore = defineStore('audio', {
state: () => ({
instances: new Set<AudioElement>(),
currentPlaying: null as AudioElement | null
}),
actions: {
// 注册音频实例
register(audio: AudioElement) {
if (!audio.uid) {
audio.uid = Symbol('audio-instance');
}
this.instances.add(audio);
},
// 注销音频实例
unregister(audio: AudioElement) {
this.instances.delete(audio);
},
// 暂停除当前音频外的所有音频
pauseAllExcept(currentAudio: AudioElement) {
this.instances.forEach(audio => {
if (audio.uid !== currentAudio.uid && !audio.paused) {
audio.pause();
audio.dispatchEvent(new Event('force-pause'));
}
});
this.currentPlaying = currentAudio;
}
}
});
状态管理设计思路
- 实例管理:使用Set存储所有音频实例,避免重复
- 唯一标识:为每个音频元素分配唯一的Symbol标识
- 互斥播放:确保同时只有一个音频在播放
- 事件通知:通过自定义事件通知组件状态变化
音频组件实现
组件Props定义
typescript
const props = defineProps({
url: String, // 音频URL
speed: String, // 播放速度
noiseSuppression: Boolean, // 降噪开关
disabled: Boolean, // 禁用状态
isbg: String, // 背景样式
mock: { // 模拟模式
type: Boolean,
default: false,
},
playData: { // 播放数据
type: Object,
default: () => ({}),
},
res: { // 响应数据
type: String,
default: '',
},
});
核心响应式数据
typescript
const audioPlayer = ref<any>(); // 音频元素引用
const audioUrl = ref(props.url); // 音频URL
const progress = ref(0); // 播放进度
const timeCount = ref(0); // 总时长(秒*10)
const startTime = ref('00:00'); // 当前时间
const endTime = ref('00:00'); // 总时长
const isPlay = ref(false); // 播放状态
关键功能实现
1. 播放控制
typescript
const playClick = () => {
if (endTime.value == '00:00') return;
if (!audioUrl.value || props.disabled || props.mock) return;
// 暂停其他音频
if (audioPlayer.value) {
audioStore.pauseAllExcept(audioPlayer.value);
}
startOrStop();
};
const startOrStop = () => {
isPlay.value = !isPlay.value;
emit('playClick', { ...props.playData, isPlay: isPlay.value, res: props.res });
if (isPlay.value) {
audioPlayer.value?.play();
// 启动进度更新定时器
time.value = setInterval(() => {
progress.value++;
startTime.value = secondToTime(progress.value / 10);
}, 100);
} else {
audioPlayer.value?.pause();
clearInterval(time.value);
clearInterval(time1.value);
}
};
2. 进度条控制
typescript
// 进度条拖拽中
const proChange = () => {
if (isDown.value) {
clearInterval(time.value);
clearInterval(time1.value);
audioPlayer.value.pause(progress.value / 10);
}
};
// 进度条拖拽结束
const proUp = () => {
isDown.value = false;
audioPlayer.value.currentTime = progress.value / 10;
audioPlayer.value.play();
clearInterval(time.value);
clearInterval(time1.value);
isPlay.value = true;
// 重新启动进度更新
time1.value = setInterval(() => {
progress.value++;
startTime.value = secondToTime(progress.value / 10);
}, 100);
};
3. 时间格式化
typescript
const secondToTime = (val: number) => {
if (!isFinite(val)) return '0:00';
val = Math.round(val);
const minutes = Math.floor(val / 60);
const seconds = val % 60;
const secondsStr = seconds < 10 ? `0${seconds}` : seconds;
return `${minutes}:${secondsStr}`;
};
4. 生命周期管理
typescript
onMounted(() => {
if (audioPlayer.value) {
// 注册到全局store
audioStore.register(audioPlayer.value);
// 监听外部暂停事件
audioPlayer.value.addEventListener('force-pause', () => {
isPlay.value = false;
clearInterval(time.value);
clearInterval(time1.value);
});
// 初始化音频时长
if (audioUrl.value) {
playAudio();
}
}
});
onUnmounted(() => {
if (audioPlayer.value) {
audioStore.unregister(audioPlayer.value);
}
});
组件模板结构
vue
<template>
<div>
<div :class="`audio-zj ${(disabled || mock) && 'audio-disabled'} ${isbg ? '' : 'isbg'}`">
<div class="a1">
<!-- 播放/暂停按钮 -->
<el-image :src="playImg" v-show="isPlay" class="a-icon" @click="playClick"></el-image>
<el-image :src="pauseImg" v-show="!isPlay" class="a-icon" @click="playClick"></el-image>
<!-- 进度条和时间显示 -->
<div class="t-bottom">
<el-slider
v-model="progress"
:show-tooltip="false"
:max="timeCount"
:disabled="disabled || mock"
class="jindu"
@input="proChange"
@change="proUp"
/>
<span class="already-time">{{ startTime }}/</span>
<span class="total-time">{{ endTime }}</span>
</div>
</div>
<!-- 隐藏的音频元素 -->
<audio
ref="audioPlayer"
class="audioPlayer"
@timeupdate="timeupdate"
:src="audioUrl"
@loadeddata="onLoad"
@ended="onEnded"
controls
></audio>
</div>
</div>
</template>
使用方法
1. 基础使用
vue
<template>
<div>
<Audio :url="audioUrl" />
</div>
</template>
<script setup>
import Audio from '@/components/audio/index.vue';
const audioUrl = ref('https://example.com/audio.mp3');
</script>
2. 完整配置
vue
<template>
<Audio
:url="item.files.url"
:speed="playbackSpeed"
:noise-suppression="true"
:disabled="isDisabled"
:mock="isMockMode"
:play-data="playData"
:res="responseData"
@play-click="handlePlayClick"
@on-load="handleAudioLoad"
@on-ended="handleAudioEnd"
/>
</template>
<script setup>
const playData = ref({
id: 'audio-1',
title: '音频标题'
});
const handlePlayClick = (data) => {
console.log('播放状态变化:', data);
};
const handleAudioLoad = () => {
console.log('音频加载完成');
};
const handleAudioEnd = () => {
console.log('音频播放结束');
};
</script>
样式定制
组件提供了丰富的CSS类名供自定义样式:
scss
.audio-zj {
// 进度条样式定制
.jindu {
.el-slider__button {
background: #f68842 !important;
border: solid 2px #f68842;
width: 11px;
height: 11px;
}
.el-slider__bar {
background-color: #f68842;
height: 3px;
}
}
// 禁用状态样式
&.audio-disabled {
cursor: not-allowed;
.a-icon {
opacity: 0.5;
}
}
}
高级特性
1. 多音频管理
通过Pinia store实现全局音频管理,确保用户体验:
typescript
// 播放新音频时自动暂停其他音频
audioStore.pauseAllExcept(audioPlayer.value);
2. 播放速度控制
typescript
watch(
() => props.speed,
newVal => {
if (audioPlayer.value) {
audioPlayer.value.playbackRate = newVal;
}
},
);
3. 降噪功能
typescript
watch(
() => props.noiseSuppression,
newVal => {
if (audioPlayer.value) {
audioPlayer.value.noiseSuppression = newVal;
}
},
);
最佳实践
- 性能优化:使用定时器更新进度时注意清理,避免内存泄漏
- 用户体验:同时只允许一个音频播放,避免声音冲突
- 错误处理:对音频加载失败、网络异常等情况进行处理
- 响应式设计:确保组件在不同设备上的显示效果
- 无障碍访问:添加适当的ARIA标签和键盘导航支持
总结
本文介绍的Vue3音频组件具有以下优势:
- 🎯 功能完整:涵盖播放控制、进度管理、时间显示等核心功能
- 🔧 高度可配置:支持多种配置选项和自定义样式
- 🚀 性能优秀:合理的状态管理和生命周期处理
- 🎨 用户友好:直观的界面设计和流畅的交互体验
- 🔒 稳定可靠:完善的错误处理和边界情况考虑
这个组件可以直接应用到各种需要音频播放功能的Vue3项目中,为用户提供优质的音频播放体验。
如果这篇文章对你有帮助,请点赞收藏!有任何问题欢迎在评论区讨论交流。如果需要音频组件整体源码的也可以私信博主。