一. 前言
在Web开发中实现音频录制功能是许多应用场景的常见需求。本文将通过一个完整的Vue 3组件示例,详细解析如何利用现代浏览器API实现网页端的录音功能。
二. 技术实现
1.核心API介绍
MediaRecorder API 是实现录音功能的核心,它允许我们直接捕获来自用户设备的媒体流。主要方法:
getUserMedia() 获取媒体设备权限
MediaRecorder() 创建录音实例
ondataavailable 接收音频数据
onstop 处理录音结束事件
2.模板部分
<template>
<div class="voice-recorder">
<!-- 录音控制 -->
<button @click="toggleRecording" :class="{ recording: isRecording }">
{{ isRecording ? "停止录音" : "开始录音" }}
</button>
<!-- 生成的音频文件列表 -->
<div v-if="audioFiles.length > 0" class="audio-list">
<div v-for="(audio, index) in audioFiles" :key="index">
<audio controls :src="audio.url"></audio>
</div>
</div>
</div>
</template>
3.核心逻辑实现
const isRecording = ref(false);
const mediaRecorder = ref(null);
const audioChunks = ref([]);
const audioFiles = ref([]);
// 开始录音
const startRecording = async (e) => {
if (isRecording.value) return;
try {
audioChunks.value = [];
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder.value = new MediaRecorder(stream);
mediaRecorder.value.ondataavailable = (e) => {
audioChunks.value.push(e.data);
};
mediaRecorder.value.onstop = () => {
const audioBlob = new Blob(audioChunks.value, { type: "audio/webm" });
const audioURL = URL.createObjectURL(audioBlob);
audioFiles.value.push({
name: `recording_${Date.now()}.${getFileExtension(audioBlob.type)}`,
url: audioURL,
});
audioChunks.value = [];
};
mediaRecorder.value.start();
isRecording.value = true;
} catch (error) {
console.error("录音错误:", error);
}
};
4. 关键功能点解析
(1) 音频格式处理
通过MIME类型映射获取文件扩展名:
// 获取文件扩展名
const getFileExtension = (mimeType) => {
const extensions = {
"audio/webm": "webm",
"audio/ogg": "ogg",
"audio/mp4": "mp4",
"audio/mpeg": "mp3",
"audio/wav": "wav",
};
return extensions[mimeType] || "webm";
};
(2) 资源管理
组件卸载时自动释放资源:
//停止录音
const stopRecording = () => {
console.log("停止录音");
isRecording.value = false;
if (mediaRecorder.value) {
mediaRecorder.value.stop();
mediaRecorder.value.stream.getTracks().forEach((track) => track.stop());
}
};
// 清理资源
onUnmounted(() => {
stopRecording();
});
三. 完整代码
<template>
<div class="voice-recorder">
<!-- 录音控制 -->
<button @click="toggleRecording" :class="{ recording: isRecording }">
{{ isRecording ? "停止录音" : "开始录音" }}
</button>
<!-- 生成的音频文件列表 -->
<div v-if="audioFiles.length > 0" class="audio-list">
<div v-for="(audio, index) in audioFiles" :key="index">
<audio controls :src="audio.url"></audio>
</div>
</div>
</div>
</template>
<script setup>
const isRecording = ref(false);
const mediaRecorder = ref(null);
const audioChunks = ref([]);
const audioFiles = ref([]);
// 开始录音
const startRecording = async (e) => {
if (isRecording.value) return;
try {
audioChunks.value = [];
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder.value = new MediaRecorder(stream);
mediaRecorder.value.ondataavailable = (e) => {
audioChunks.value.push(e.data);
};
mediaRecorder.value.onstop = () => {
const audioBlob = new Blob(audioChunks.value, { type: "audio/webm" });
const audioURL = URL.createObjectURL(audioBlob);
audioFiles.value.push({
name: `recording_${Date.now()}.${getFileExtension(audioBlob.type)}`,
url: audioURL,
});
audioChunks.value = [];
};
mediaRecorder.value.start();
isRecording.value = true;
} catch (error) {
console.error("录音错误:", error);
}
};
//停止录音
const stopRecording = () => {
console.log("停止录音");
isRecording.value = false;
if (mediaRecorder.value) {
mediaRecorder.value.stop();
mediaRecorder.value.stream.getTracks().forEach((track) => track.stop());
}
};
// 获取文件扩展名
const getFileExtension = (mimeType) => {
const extensions = {
"audio/webm": "webm",
"audio/ogg": "ogg",
"audio/mp4": "mp4",
"audio/mpeg": "mp3",
"audio/wav": "wav",
};
return extensions[mimeType] || "webm";
};
// 切换录音状态
const toggleRecording = () => {
if (!isRecording.value) {
startRecording();
} else {
if (mediaRecorder.value) {
mediaRecorder.value.stop();
mediaRecorder.value.stream.getTracks().forEach((track) => track.stop());
}
}
isRecording.value = !isRecording.value;
};
// 清理资源
onUnmounted(() => {
stopRecording();
});
</script>
<style lang="scss" scoped>
.voice-recorder {
max-width: 800px;
margin: 0 auto;
padding: 20px;
button {
padding: 12px 24px;
background: #42b983;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background 0.3s;
&.recording {
background: #ff4444;
}
}
button:hover {
background: #3aa876;
}
button:disabled {
background: #ddd;
cursor: not-allowed;
}
}
.recording {
background-color: #ff4444;
color: white;
}
.audio-list {
margin-top: 20px;
}
</style>
四. 功能扩展建议
添加录音时长显示
实现音频波形可视化