返回

Vue.js 语音识别按钮失效问题排查与解决指南

vue.js

Vue.js 语音识别按钮失效问题排查与解决

最近写了个 Vue.js 的语音指令应用,发现语音按钮按一次就失效了,必须刷新页面才能再次使用。这可不行,用户体验太差了,必须解决!

问题:语音识别后按钮持续禁用

我的语音应用中有一个“说话”按钮,点击后开始语音识别。问题在于,识别完成后,按钮仍然处于禁用 (disabled) 状态,无法再次点击。代码如下:

<div class="input">
  <button
    class="talk"
    :disabled="isRecording"
    @click="startListening"
  >
    <i class="fas fa-microphone-alt" />
  </button>
  <h1
    class="content"
    @click="startListening"
  >
    Konuş
  </h1>
</div>

问题原因分析

isRecording 变量控制按钮的 :disabled 属性。按钮点击后触发 startListening 方法,该方法会启动语音识别,并将isRecording设置为 true, 禁用按钮. 语音识别结束后,isRecording 的值理论上应变为false,让按钮解除禁用。 我尝试过的停止方法如下

 stopRecognition() {
      const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)()

      recognition.stop() // Stop speech recognition
      this.isRecording = false // Set recording status to false
    },

直接调用 recognition.stop() 停止识别,然后isRecording = false。看似没问题,但是按钮还是用不了。这意味着: 要么stopRecognition() 没有成功执行;要么语音识别没有正确停止或有额外的异步操作影响了 isRecording 的状态;又或者有其他逻辑导致isRecording被重置回了true.

解决方案

下面,我将逐步排查并提供几个可行的解决办法。

1. 确保 stopRecognition 方法正确调用

首先要保证stopRecognition方法被正确调用到。我用的 SpeechRecognition API,停止语音识别需要在语音识别的回调事件(比如 onendonresult) 中去执行stopRecognition,而不是在代码中任何位置想当然的调用.

  • 原理: SpeechRecognition API 是异步的,recognition.start() 启动后,识别过程在后台进行。我们需要监听它的事件来确定识别状态的变化。
  • 代码示例:
export default {
  data() {
    return {
      isRecording: false,
      recognition: null, // 保存 recognition 实例
    };
  },
  methods: {
    startListening() {
      this.recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
      this.recognition.lang = 'tr-TR'; //或者en-US, 根据实际需要

      this.recognition.onstart = () => {
        this.isRecording = true;
        console.log('Voice recognition started. Try speaking into the microphone.');
      };

      this.recognition.onend = () => {
          this.stopRecognition();
          console.log("Voice Recogntion Ended")
      };

      this.recognition.onresult = (event) => {
        //处理识别结果
         const transcript = event.results[event.results.length - 1][0].transcript;
          console.log(transcript)

      };

        this.recognition.onerror = (event) => {
          console.error('语音识别错误:', event.error);
           this.stopRecognition();
        }

      this.recognition.start();
    },
    stopRecognition() {

        if (this.recognition) {
            this.recognition.stop();
            this.isRecording = false;
        }
    },
  },
     beforeDestroy() {
    // 在组件销毁前停止识别,防止内存泄漏
      if (this.recognition) {
           this.stopRecognition()
      }
  }
};
  • 操作说明:
    1. data 中初始化 recognitionnull
    2. startListening 方法中创建 SpeechRecognition 实例,并将其赋值给 this.recognition
    3. 监听 onstartonendonresultonerror事件。
    4. onendonerror 事件处理函数中调用 stopRecognition 方法。
    5. stopRecognition内先检查this.recognition 是否存在。
    6. 组件的 beforeDestroy 钩子中调用一次stopRecognition. 防止组件被销毁了,识别还在继续。

2. 检查异步操作

如果 stopRecognition 已经正确调用,按钮仍然禁用,可能是存在其他的异步操作导致isRecording在之后被错误地设置回true

  • 原理: JavaScript 的异步操作(如 Promise、setTimeout 等)可能会导致代码执行顺序与预期不符,从而影响变量的值。
  • 代码示例(假设有个异步操作重置了isRecording):
    这里只是做一个示例,因为无法得知项目实际的异步操作, 所以只展示一种思路
    startListening() {
          this.recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
      this.recognition.lang = 'tr-TR'; //或者en-US, 根据实际需要

       this.recognition.onstart = () => {
        this.isRecording = true;
         console.log('Voice recognition started. Try speaking into the microphone.');
          // 假设有个异步操作
        setTimeout(() => {
              if(this.recognition && this.recognition.continuous){ //增加额外判断
                this.isRecording = true;
                console.warn("isRecording was reset by a timeout!");
              }

        }, 5000); // 5 秒后执行, 这里仅仅是假设! 只是用来展示如果出现类似问题该怎么办
      };


      this.recognition.onend = () => {
          this.stopRecognition();
          console.log("Voice Recogntion Ended")
      };

        this.recognition.onresult = (event) => {
          const transcript = event.results[event.results.length - 1][0].transcript;
          console.log(transcript)

        };

      this.recognition.onerror = (event) => {
        console.error('语音识别错误:', event.error);
          this.stopRecognition()
      }

      this.recognition.start();
    },
  • 操作说明:
  1. 使用 console.log 或 Vue Devtools 调试工具仔细观察 isRecording 的变化过程, 确认它没有被意外重置.
  2. 仔细审查代码,尤其是在 startListening 方法和相关的事件处理函数中,找出所有可能修改 isRecording 的地方。

3. 使用 continuous 属性(进阶)

SpeechRecognition 有一个 continuous 属性,控制是否连续识别。如果 continuoustrue(默认值),即使识别出一句话,识别过程也不会自动停止, onend 可能不会立即触发。

  • 原理: continuoustrue 时,SpeechRecognition 会持续监听,直到手动调用 stop() 方法。这可能会导致isRecording 持续为true.
  • 代码示例:
    startListening() {
     this.recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
      this.recognition.lang = 'tr-TR'; // 根据你的实际需求
      this.recognition.continuous = false; // 设置为 false,单次识别

        this.recognition.onstart = () => {
        this.isRecording = true;
      };


      this.recognition.onend = () => {
          this.stopRecognition();
      };

        this.recognition.onresult = (event) => {
            //处理结果
            const transcript = event.results[event.results.length - 1][0].transcript;
            console.log(transcript);
              this.stopRecognition() // 处理完结果也立即停止
        };

      this.recognition.onerror = (event) => {
        console.error('语音识别错误:', event.error);
        this.stopRecognition()
      }
      this.recognition.start();
    },
  • 操作说明:

    1. 在创建 SpeechRecognition 实例时,将 continuous 属性设置为 false
    2. 确认onresult 中执行stopRecognition,处理完结果后立即停止识别。

4. 检查是否存在多次 start 的情况 (进阶)

在高频点击或者一些特殊逻辑下, 有可能会多次调用startListening,导致创建了多个 SpeechRecognition 实例,旧的实例可能还在运行,干扰了新实例。

  • 原理: 多个 SpeechRecognition 实例同时运行可能导致状态混乱, 事件处理交叉。
  • 代码示例:
 methods: {
    startListening() {

        // 停止之前的识别(如果有)
        if (this.recognition) {
            this.recognition.onend = null; //先移除旧的事件监听, 很重要
             this.recognition.onerror = null;
             this.recognition.onresult = null;
              this.recognition.stop(); //确保之前的识别停掉.
        }

      this.recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
      // ... 其他代码 ...
       this.recognition.onstart = () => {
        this.isRecording = true;
      };


      this.recognition.onend = () => {
          this.stopRecognition();
      };

        this.recognition.onresult = (event) => {
            //处理结果
             const transcript = event.results[event.results.length - 1][0].transcript;
            console.log(transcript);
            this.stopRecognition() // 处理完结果也立即停止
        };

        this.recognition.onerror = (event) => {
             this.stopRecognition(); //识别出错了也要停止,
           console.error("error occurred: ", event.error);
        }

       this.recognition.start()
    },
    stopRecognition() {

        if (this.recognition) {
              this.recognition.onend = null; // 移除事件监听
              this.recognition.onerror = null;
              this.recognition.onresult = null;
            this.recognition.stop();
            this.isRecording = false;

        }
    },
     beforeDestroy() {

       this.stopRecognition();
  }
  }
  • 操作说明
    1. startListening 开始时,先检查 this.recognition 是否存在,如果存在,先调用stop 停止之前的识别. 并且移除旧的事件监听器, 防止多个实例的事件混淆。
    2. stopRecognition 内部,停止识别后, 也要将之前的事件监听移除。

安全建议:

  • 用户隐私: 语音识别涉及到用户语音数据,请确保你的应用符合相关的隐私法规,并在必要时向用户明确说明。
  • HTTPS: 语音识别通常需要通过网络传输数据,强烈建议使用 HTTPS 来保护数据安全。
  • 错误处理 : 对于onerror 事件一定要处理. 不要忽略错误,因为SpeechRecognition 可能会遇到各种问题,例如网络连接问题、麦克风权限问题等.

通过以上几种方案的排查和尝试,相信你一定能解决 Vue.js 语音识别按钮失效的问题。让你的应用更流畅,用户体验更棒!