Vue 可组合项实战:从零构建语音识别功能

Vue 可组合项实战:从零构建语音识别功能

一、为什么选择Vue组合式API开发语音识别?

Vue 3的组合式API(Composition API)为复杂功能开发提供了更灵活的组织方式。相比选项式API,组合式API通过setup()函数将逻辑按功能而非选项类型分组,特别适合需要管理多个状态和副作用的场景,如语音识别这类涉及浏览器API调用、状态管理和UI交互的功能。

核心优势:

  1. 逻辑复用性:通过自定义组合函数(composables)封装语音识别逻辑,可在多个组件中共享
  2. 类型友好性:与TypeScript深度集成,适合中大型项目
  3. 响应式系统:利用refreactive精确控制识别状态
  4. 生命周期明确:通过onMounted/onUnmounted管理资源

二、技术栈准备

1. 浏览器API依赖

现代浏览器提供的Web Speech API包含两个核心接口:

  • SpeechRecognition(识别):将语音转为文本
  • SpeechSynthesis(合成):将文本转为语音

注意:目前仅Chrome、Edge、Safari等浏览器支持,需做兼容性检测

2. 开发环境配置

  1. npm init vue@latest vue-speech-demo
  2. cd vue-speech-demo
  3. npm install

vite.config.js中添加TypeScript支持(可选):

  1. export default defineConfig({
  2. plugins: [vue()],
  3. resolve: {
  4. alias: {
  5. '@': path.resolve(__dirname, './src')
  6. }
  7. }
  8. })

三、核心组合函数实现

1. 创建useSpeechRecognition.ts

  1. import { ref, onUnmounted } from 'vue'
  2. export function useSpeechRecognition() {
  3. const recognition = ref<SpeechRecognition | null>(null)
  4. const isListening = ref(false)
  5. const transcript = ref('')
  6. const error = ref<string | null>(null)
  7. const initRecognition = () => {
  8. const SpeechRecognition = window.SpeechRecognition ||
  9. (window as any).webkitSpeechRecognition
  10. if (!SpeechRecognition) {
  11. error.value = '浏览器不支持语音识别'
  12. return null
  13. }
  14. const instance = new SpeechRecognition()
  15. instance.continuous = true
  16. instance.interimResults = true
  17. instance.lang = 'zh-CN' // 中文识别
  18. instance.onresult = (event: SpeechRecognitionEvent) => {
  19. let interimTranscript = ''
  20. let finalTranscript = ''
  21. for (let i = event.resultIndex; i < event.results.length; i++) {
  22. const transcriptPiece = event.results[i][0].transcript
  23. if (event.results[i].isFinal) {
  24. finalTranscript += transcriptPiece + ' '
  25. } else {
  26. interimTranscript += transcriptPiece
  27. }
  28. }
  29. transcript.value = finalTranscript || interimTranscript
  30. }
  31. instance.onerror = (event: SpeechRecognitionErrorEvent) => {
  32. error.value = `识别错误: ${event.error}`
  33. stopListening()
  34. }
  35. instance.onend = () => {
  36. if (isListening.value) {
  37. instance.start() // 自动重启(根据需求调整)
  38. }
  39. }
  40. recognition.value = instance
  41. return instance
  42. }
  43. const startListening = () => {
  44. if (!recognition.value) {
  45. initRecognition()
  46. }
  47. recognition.value?.start()
  48. isListening.value = true
  49. error.value = null
  50. }
  51. const stopListening = () => {
  52. recognition.value?.stop()
  53. isListening.value = false
  54. }
  55. // 组件卸载时清理
  56. onUnmounted(() => {
  57. stopListening()
  58. })
  59. return {
  60. transcript,
  61. isListening,
  62. error,
  63. startListening,
  64. stopListening
  65. }
  66. }

2. 关键点解析

  1. 浏览器兼容处理:通过类型断言(as any)处理不同浏览器前缀
  2. 状态管理:使用ref创建响应式变量
  3. 事件处理
    • onresult:处理实时识别结果
    • onerror:捕获异常
    • onend:处理识别结束
  4. 资源清理:在onUnmounted中停止识别

四、组件实现与优化

1. 创建SpeechRecognizer.vue

  1. <script setup lang="ts">
  2. import { useSpeechRecognition } from './composables/useSpeechRecognition'
  3. const {
  4. transcript,
  5. isListening,
  6. error,
  7. startListening,
  8. stopListening
  9. } = useSpeechRecognition()
  10. </script>
  11. <template>
  12. <div class="speech-container">
  13. <div v-if="error" class="error-message">
  14. {{ error }}
  15. </div>
  16. <div class="transcript-area">
  17. {{ transcript || '等待语音输入...' }}
  18. </div>
  19. <button
  20. @click="isListening ? stopListening() : startListening()"
  21. :disabled="!!error"
  22. class="control-btn"
  23. >
  24. {{ isListening ? '停止识别' : '开始识别' }}
  25. </button>
  26. </div>
  27. </template>
  28. <style scoped>
  29. .speech-container {
  30. max-width: 600px;
  31. margin: 0 auto;
  32. padding: 20px;
  33. }
  34. .transcript-area {
  35. min-height: 100px;
  36. border: 1px solid #ddd;
  37. padding: 15px;
  38. margin: 20px 0;
  39. border-radius: 8px;
  40. background: #f9f9f9;
  41. }
  42. .control-btn {
  43. padding: 10px 20px;
  44. background: #42b983;
  45. color: white;
  46. border: none;
  47. border-radius: 4px;
  48. cursor: pointer;
  49. }
  50. .control-btn:disabled {
  51. background: #cccccc;
  52. cursor: not-allowed;
  53. }
  54. .error-message {
  55. color: #ff4444;
  56. margin-bottom: 15px;
  57. }
  58. </style>

2. 高级优化技巧

  1. 语言切换:通过props支持多语言识别

    1. // 在composable中修改
    2. export function useSpeechRecognition(lang = 'zh-CN') {
    3. // ...
    4. instance.lang = lang
    5. // ...
    6. }
  2. 识别结果处理:添加结果过滤和格式化

    1. const formatTranscript = (rawText: string) => {
    2. return rawText
    3. .trim()
    4. .replace(/\s+/g, ' ') // 去除多余空格
    5. .replace(/[。!?]+[,、]*/g, '$& ') // 优化标点
    6. }
  3. 性能优化:添加防抖处理连续识别
    ```typescript
    import { debounce } from ‘lodash-es’

// 在onresult处理中
const processResult = debounce((event) => {
// 处理逻辑
}, 300)

instance.onresult = (event) => {
processResult(event)
}

  1. ## 五、部署与测试
  2. ### 1. 兼容性检测
  3. `main.ts`中添加检测逻辑:
  4. ```typescript
  5. const checkSpeechAPI = () => {
  6. if (!('SpeechRecognition' in window) &&
  7. !('webkitSpeechRecognition' in window)) {
  8. alert('当前浏览器不支持语音识别功能,请使用Chrome/Edge/Safari')
  9. }
  10. }
  11. checkSpeechAPI()

2. 测试用例设计

测试场景 预期结果
点击开始按钮 麦克风权限请求弹出
正常说话 实时显示识别文本
长时间不说话 自动停止识别(根据continuous设置)
切换标签页 识别暂停(部分浏览器行为)
不支持浏览器访问 显示错误提示

六、扩展应用场景

  1. 语音搜索:结合后端API实现实时搜索
  2. 语音笔记:将识别结果保存到本地存储
  3. 无障碍功能:为视障用户提供语音导航
  4. IoT控制:通过语音指令控制智能家居设备

七、最佳实践总结

  1. 错误处理:始终捕获onerror事件并提供用户反馈
  2. 资源管理:在组件卸载时清理识别实例
  3. 状态明确:通过isListening清晰反映当前状态
  4. 性能考量:对连续识别结果进行适当节流
  5. 用户体验:提供视觉反馈(如麦克风动画)

通过组合式API开发的语音识别功能,不仅代码结构清晰,而且易于维护和扩展。开发者可以根据实际需求,轻松添加更多功能如语音命令解析、多语言支持等。这种模式特别适合需要复用语音识别逻辑的中大型Vue应用。