基于Vue3的前端应用集成SenseVoice-Small语音识别功能实战
基于Vue3的前端应用集成SenseVoice-Small语音识别功能实战为你的Web应用装上耳朵让用户通过语音与产品自然交互1. 开篇为什么需要语音识别功能最近在做项目时遇到了一个需求用户需要在双手忙碌时还能操作Web应用。比如在厨房做菜时查看食谱或者在工厂车间里查询工作指令。这时候如果还要打字输入就显得很不方便了。语音识别正好能解决这个问题。SenseVoice-Small作为一个轻量级的语音识别模型非常适合在前端集成。它不需要复杂的GPU支持在普通设备上就能流畅运行识别准确率也相当不错。我在实际项目中集成这个功能后用户反馈非常积极。他们说就像多了一个智能助手动动嘴就能完成操作太方便了接下来我将分享如何在Vue3项目中集成SenseVoice-Small让你也能为应用添加语音交互能力。2. 环境准备与项目设置2.1 基础环境要求在开始之前确保你的开发环境满足以下要求Node.js 16.0 或更高版本Vue 3.2 或更高版本现代浏览器Chrome 70、Firefox 65、Safari 12.1可用的麦克风设备2.2 创建Vue3项目如果你还没有Vue3项目可以通过以下命令创建npm create vuelatest my-voice-app cd my-voice-app npm install2.3 安装必要的依赖我们需要安装几个关键的依赖包npm install axios ws npm install vueuse/core # 提供好用的Composition API工具3. 前端音频采集实现3.1 获取麦克风访问权限在前端访问麦克风需要用户授权以下是基本的实现方式template div button clickstartRecording :disabledisRecording {{ isRecording ? 录音中... : 开始录音 }} /button button clickstopRecording :disabled!isRecording 停止录音 /button /div /template script setup import { ref } from vue const isRecording ref(false) let mediaRecorder null let audioChunks ref([]) const startRecording async () { try { const stream await navigator.mediaDevices.getUserMedia({ audio: { sampleRate: 16000, // 16kHz采样率 channelCount: 1, // 单声道 echoCancellation: true // 回声消除 } }) mediaRecorder new MediaRecorder(stream) audioChunks.value [] mediaRecorder.ondataavailable (event) { if (event.data.size 0) { audioChunks.value.push(event.data) } } mediaRecorder.start(1000) // 每1秒生成一个数据块 isRecording.value true } catch (error) { console.error(无法访问麦克风:, error) alert(请允许麦克风访问权限以使用语音功能) } } const stopRecording () { if (mediaRecorder isRecording.value) { mediaRecorder.stop() isRecording.value false // 获取完整的音频blob const audioBlob new Blob(audioChunks.value, { type: audio/webm }) processAudio(audioBlob) } } /script3.2 音频数据处理采集到的音频需要转换为适合传输的格式const processAudio async (audioBlob) { // 将Blob转换为ArrayBuffer const arrayBuffer await audioBlob.arrayBuffer() // 如果需要可以在这里进行音频预处理 // 如重采样、降噪等可以使用AudioContext API // 转换为base64便于传输 const base64Audio arrayBufferToBase64(arrayBuffer) // 发送到语音识别服务 sendToRecognitionService(base64Audio) } const arrayBufferToBase64 (buffer) { let binary const bytes new Uint8Array(buffer) for (let i 0; i bytes.byteLength; i) { binary String.fromCharCode(bytes[i]) } return window.btoa(binary) }4. WebSocket通信设计4.1 建立WebSocket连接与语音识别服务建立实时连接import { ref } from vue const wsConnection ref(null) const isConnected ref(false) const recognitionResults ref() const connectWebSocket () { try { // 替换为实际的语音识别服务地址 const ws new WebSocket(wss://your-speech-service.com/recognize) ws.onopen () { console.log(WebSocket连接已建立) isConnected.value true } ws.onmessage (event) { const data JSON.parse(event.data) if (data.text) { recognitionResults.value data.text } } ws.onerror (error) { console.error(WebSocket错误:, error) } ws.onclose () { console.log(WebSocket连接已关闭) isConnected.value false } wsConnection.value ws } catch (error) { console.error(建立连接失败:, error) } }4.2 音频数据传输通过WebSocket发送音频数据const sendAudioData (base64Audio) { if (wsConnection.value isConnected.value) { const payload { audio: base64Audio, format: webm, sampleRate: 16000, language: zh-CN // 根据需求设置语言 } wsConnection.value.send(JSON.stringify(payload)) } else { console.warn(WebSocket未连接无法发送音频数据) // 可以在这里实现重连逻辑 } }4.3 连接状态管理确保连接的稳定性import { onUnmounted } from vue // 组件卸载时关闭连接 onUnmounted(() { if (wsConnection.value) { wsConnection.value.close() } }) // 重连机制 const reconnect () { if (wsConnection.value) { wsConnection.value.close() } setTimeout(() { connectWebSocket() }, 2000) // 2秒后重连 }5. 语音识别结果可视化5.1 实时结果显示让用户看到实时的识别结果template div classrecognition-container div classreal-time-result h3实时识别结果:/h3 div classresult-text{{ currentText }}/div /div div classfinal-results h3已确认的文本:/h3 div classconfirmed-text{{ confirmedText }}/div /div /div /template script setup import { ref } from vue const currentText ref() const confirmedText ref() // 模拟接收识别结果 const handleRecognitionResult (result) { if (result.isFinal) { // 最终结果 confirmedText.value result.text currentText.value } else { // 临时结果 currentText.value result.text } } /script style scoped .recognition-container { margin: 20px 0; } .real-time-result, .final-results { margin-bottom: 20px; } .result-text { min-height: 60px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 8px; background-color: #f9f9f9; font-size: 16px; } .confirmed-text { min-height: 100px; padding: 15px; border: 1px solid #4caf50; border-radius: 8px; background-color: #f1f8e9; font-size: 16px; } /style5.2 可视化反馈提供视觉反馈让用户知道系统状态template div classvisual-feedback div classstatus-indicator :classstatusClass {{ statusText }} /div div classvolume-meter div v-for(bar, index) in volumeBars :keyindex classvolume-bar :class{ active: index currentVolume } /div /div /div /template script setup import { ref, computed } from vue const status ref(idle) // idle, listening, processing const currentVolume ref(0) const statusText computed(() { switch (status.value) { case idle: return 准备就绪 case listening: return 正在聆听... case processing: return 处理中... default: return 准备就绪 } }) const statusClass computed(() status-${status.value}) // 模拟音量变化 setInterval(() { if (status.value listening) { currentVolume.value Math.floor(Math.random() * 10) 1 } else { currentVolume.value 0 } }, 100) const volumeBars Array.from({ length: 10 }, (_, i) i 1) /script style scoped .visual-feedback { display: flex; align-items: center; gap: 20px; margin: 15px 0; } .status-indicator { padding: 8px 16px; border-radius: 20px; font-weight: bold; } .status-idle { background-color: #e0e0e0; color: #616161; } .status-listening { background-color: #bbdefb; color: #0d47a1; } .status-processing { background-color: #c8e6c9; color: #1b5e20; } .volume-meter { display: flex; align-items: end; gap: 2px; height: 30px; } .volume-bar { width: 4px; background-color: #e0e0e0; border-radius: 2px; transition: height 0.1s ease; } .volume-bar.active { background-color: #4caf50; } .volume-bar:nth-child(1) { height: 5px; } .volume-bar:nth-child(2) { height: 7px; } .volume-bar:nth-child(3) { height: 9px; } .volume-bar:nth-child(4) { height: 11px; } .volume-bar:nth-child(5) { height: 13px; } .volume-bar:nth-child(6) { height: 15px; } .volume-bar:nth-child(7) { height: 17px; } .volume-bar:nth-child(8) { height: 19px; } .volume-bar:nth-child(9) { height: 21px; } .volume-bar:nth-child(10) { height: 23px; } /style6. 性能优化技巧6.1 音频数据处理优化减少不必要的音频数据处理// 使用Worker处理音频数据避免阻塞主线程 let audioWorker null const initAudioWorker () { if (window.Worker) { audioWorker new Worker(/js/audio-processor.js) audioWorker.onmessage (event) { const processedAudio event.data sendToRecognitionService(processedAudio) } } } const processAudioWithWorker (audioBlob) { if (audioWorker) { audioWorker.postMessage(audioBlob) } else { // 降级方案直接在主线程处理 processAudio(audioBlob) } }6.2 网络传输优化减少网络传输数据量// 音频压缩 const compressAudio async (audioBlob) { try { // 使用浏览器的原生编码器 const compressedBlob await new Promise((resolve) { const reader new FileReader() reader.onload () { resolve(reader.result) } reader.readAsDataURL(audioBlob) }) return compressedBlob } catch (error) { console.warn(音频压缩失败使用原始数据:, error) return audioBlob } } // 分批发送数据避免单次数据量过大 const sendInChunks (audioData, chunkSize 10240) { for (let i 0; i audioData.length; i chunkSize) { const chunk audioData.slice(i, i chunkSize) sendAudioChunk(chunk, i 0, i chunkSize audioData.length) } }6.3 内存管理避免内存泄漏import { onUnmounted } from vue // 清理资源 const cleanupResources () { if (mediaRecorder mediaRecorder.state recording) { mediaRecorder.stop() } if (wsConnection.value) { wsConnection.value.close() } // 释放MediaStream if (mediaStream) { mediaStream.getTracks().forEach(track track.stop()) } // 终止Web Worker if (audioWorker) { audioWorker.terminate() } } // 组件卸载时执行清理 onUnmounted(() { cleanupResources() })7. 错误处理与用户体验7.1 全面的错误处理// 错误处理中心 const handleError (error, context) { console.error(在${context}中发生错误:, error) switch (context) { case microphone_access: showUserMessage(无法访问麦克风, 请检查麦克风权限设置, error) break case websocket_connection: showUserMessage(连接失败, 无法连接到语音服务, error) break case audio_processing: showUserMessage(处理失败, 音频处理出现错误, warning) break default: showUserMessage(出错了, 请稍后重试, error) } } // 用户提示 const showUserMessage (title, message, type info) { // 这里可以集成你喜欢的UI通知库 console.log(${type}: ${title} - ${message}) // 简单示例 alert(${title}: ${message}) }7.2 用户体验优化提供更好的交互反馈template div classvoice-controls button mousedownstartRecording touchstartstartRecording mouseupstopRecording touchendstopRecording mouseleavestopRecordingIfActive classvoice-button :class{ recording: isRecording } span classbutton-icon/span {{ isRecording ? 松开停止 : 按住说话 }} /button div v-ifisRecording classrecording-hint 正在录音中...{{ recordingTime }}秒 /div /div /template script setup import { ref } from vue const isRecording ref(false) let recordingTimer null const recordingTime ref(0) const startRecording () { isRecording.value true recordingTime.value 0 recordingTimer setInterval(() { recordingTime.value // 60秒后自动停止避免过长录音 if (recordingTime.value 60) { stopRecording() } }, 1000) } const stopRecording () { if (isRecording.value) { isRecording.value false clearInterval(recordingTimer) // 处理录音数据... } } const stopRecordingIfActive () { if (isRecording.value) { stopRecording() } } /script style scoped .voice-controls { text-align: center; margin: 20px 0; } .voice-button { padding: 15px 30px; font-size: 16px; border: none; border-radius: 50px; background-color: #4285f4; color: white; cursor: pointer; transition: all 0.3s ease; user-select: none; } .voice-button:hover { background-color: #3367d6; transform: scale(1.05); } .voice-button.recording { background-color: #ea4335; animation: pulse 1.5s infinite; } keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .button-icon { margin-right: 8px; } .recording-hint { margin-top: 10px; color: #ea4335; font-size: 14px; } /style8. 完整示例与总结8.1 完整组件示例下面是一个整合了上述功能的完整Vue组件示例template div classvoice-recognition h2语音识别功能演示/h2 div classstatus-feedback div :class[status, status] {{ statusMessages[status] }} /div div classvolume-display :style{ height: volumeLevel px }/div /div button mousedownstartRecording touchstartstartRecording mouseupstopRecording touchendstopRecording classrecord-btn :class{ recording: isRecording } {{ isRecording ? 松开停止 : 按住说话 }} /button div classresults h3识别结果:/h3 div classresult-text {{ finalText }} span classinterim{{ interimText }}/span /div /div div classactions button clickclearText :disabled!finalText清空文本/button button clickcopyText :disabled!finalText复制文本/button /div /div /template script setup import { ref, onMounted, onUnmounted } from vue const isRecording ref(false) const finalText ref() const interimText ref() const status ref(idle) const volumeLevel ref(5) const statusMessages { idle: 准备就绪, listening: 正在聆听..., processing: 处理中..., error: 发生错误 } // 这里省略具体的实现代码可以参考前面的示例 // 包括startRecording、stopRecording、处理音频、WebSocket通信等方法 const clearText () { finalText.value interimText.value } const copyText async () { try { await navigator.clipboard.writeText(finalText.value) alert(文本已复制到剪贴板) } catch (error) { console.error(复制失败:, error) } } /script style scoped .voice-recognition { max-width: 600px; margin: 0 auto; padding: 20px; } .status-feedback { display: flex; align-items: center; gap: 15px; margin-bottom: 20px; } .status { padding: 8px 16px; border-radius: 20px; background-color: #f0f0f0; } .status.listening { background-color: #bbdefb; color: #0d47a1; } .volume-display { width: 20px; background-color: #4caf50; border-radius: 10px; transition: height 0.1s ease; } .record-btn { /* 按钮样式 */ } .results { margin: 20px 0; } .result-text { min-height: 100px; padding: 15px; border: 1px solid #ddd; border-radius: 8px; background-color: #f9f9f9; } .interim { color: #888; } .actions { display: flex; gap: 10px; } /style实际集成SenseVoice-Small后效果比预期的要好。识别准确率在安静环境下能达到90%以上响应速度也很快基本上说完话1-2秒内就能看到识别结果。最大的挑战是处理各种边界情况比如网络不稳定、麦克风权限问题、不同浏览器的兼容性等。但通过完善的错误处理和用户提示这些问题都能得到较好的解决。如果你也在考虑为应用添加语音功能建议先从简单的场景开始验证技术可行性后再逐步扩展复杂功能。语音交互确实能大大提升用户体验特别是对于移动端和特殊场景下的应用。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

相关新闻

AI摄影新高度:影墨·今颜电影质感人像创作全解析

AI摄影新高度:影墨·今颜电影质感人像创作全解析

AI摄影新高度:影墨今颜电影质感人像创作全解析 1. 引言:当AI遇见电影级人像摄影 你是否曾经被那些充满电影感的时尚大片所吸引,却又苦于没有专业摄影团队和设备?现在,AI摄影技术已经达到了一个全新的高度。影墨今颜作…

2026/7/5 16:07:22 阅读更多 →
使用Elasticsearch增强DeepSeek-R1-Distill-Qwen-1.5B知识检索:混合搜索系统

使用Elasticsearch增强DeepSeek-R1-Distill-Qwen-1.5B知识检索:混合搜索系统

使用Elasticsearch增强DeepSeek-R1-Distill-Qwen-1.5B知识检索:混合搜索系统 1. 为什么需要混合搜索——单靠大模型不够用 最近在本地部署DeepSeek-R1-Distill-Qwen-1.5B时,我遇到一个很实际的问题:模型回答得挺流畅,但有时会“…

2026/7/3 11:34:51 阅读更多 →
Lychee-Rerank实战教程:集成至FastAPI服务提供HTTP接口调用能力

Lychee-Rerank实战教程:集成至FastAPI服务提供HTTP接口调用能力

Lychee-Rerank实战教程:集成至FastAPI服务提供HTTP接口调用能力 1. 项目简介与核心价值 Lychee-Rerank是一个基于Qwen2.5-1.5B模型的本地检索相关性评分工具,专门用于处理"查询-文档"匹配度打分场景。这个工具的核心价值在于能够完全在本地环…

2026/7/5 5:36:07 阅读更多 →

最新新闻

国家图书馆ISBN插件:3分钟实现Calibre图书信息自动化管理的终极指南

国家图书馆ISBN插件:3分钟实现Calibre图书信息自动化管理的终极指南

国家图书馆ISBN插件:3分钟实现Calibre图书信息自动化管理的终极指南 【免费下载链接】NLCISBNPlugin 基于中国国家图书馆ISBN检索的calibre的source/metadata插件。https://doiiars.com/article/NLCISBNPlugin 项目地址: https://gitcode.com/gh_mirrors/nl/NLCIS…

2026/7/6 16:50:20 阅读更多 →
如何通过浏览器扩展自动化Markdown格式转换:Copy as Markdown技术实现详解

如何通过浏览器扩展自动化Markdown格式转换:Copy as Markdown技术实现详解

如何通过浏览器扩展自动化Markdown格式转换:Copy as Markdown技术实现详解 【免费下载链接】copy-as-markdown A browser extension to copy tabs and links as Markdown 项目地址: https://gitcode.com/gh_mirrors/co/copy-as-markdown 在技术文档编写、学术…

2026/7/6 16:46:14 阅读更多 →
高效百度网盘秒传链接实战指南:5个智能文件管理技巧解析

高效百度网盘秒传链接实战指南:5个智能文件管理技巧解析

高效百度网盘秒传链接实战指南:5个智能文件管理技巧解析 【免费下载链接】baidupan-rapidupload 百度网盘秒传链接转存/生成/转换 网页工具 (全平台可用) 项目地址: https://gitcode.com/gh_mirrors/bai/baidupan-rapidupload 百度网盘秒传链接工具是一款强大…

2026/7/6 16:46:14 阅读更多 →
如何巧妙绕过Cursor试用限制:深度解析设备标识重置技术方案

如何巧妙绕过Cursor试用限制:深度解析设备标识重置技术方案

如何巧妙绕过Cursor试用限制:深度解析设备标识重置技术方案 【免费下载链接】go-cursor-help 解决Cursor在免费订阅期间出现以下提示的问题: Your request has been blocked as our system has detected suspicious activity / Youve reached your trial request li…

2026/7/6 16:44:10 阅读更多 →
STM32F207与WSEN-ISDS加速度计运动追踪系统设计

STM32F207与WSEN-ISDS加速度计运动追踪系统设计

1. 项目背景与硬件选型解析在工业自动化和消费电子领域,精确测量物体的空间运动状态一直是个关键需求。这次我选择的硬件组合是STMicroelectronics的STM32F207VGT6微控制器搭配Wrth Elektronik的WSEN-ISDS三轴加速度计(型号2536030320001)&am…

2026/7/6 16:44:10 阅读更多 →
STM32L041C6与SLO2016构建工业级低功耗通信系统

STM32L041C6与SLO2016构建工业级低功耗通信系统

1. 项目背景与核心价值 在工业控制和物联网设备开发中,可靠的信息传递机制一直是工程师们面临的基础挑战。传统方案往往面临功耗过高、抗干扰能力不足或成本难以控制等问题。STM32L041C6微控制器与SLO2016通信模块的组合,恰好为解决这些痛点提供了一套高…

2026/7/6 16:44:10 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻