3. Web Speech API 入门:SpeechRecognition 对象详解
好,咱们进入实战环节了。前面两章聊了语音识别的基本概念和浏览器兼容性,现在该动手了。Web Speech API 是浏览器原生提供的语音识别接口,说白了,就是让网页能“听懂”人话。我最早接触这个 API 是在 2016 年,那时候 Chrome 刚支持,坑还不少。现在稳定多了,但有些细节还是得注意。
3.1 SpeechRecognition 对象:一切从这里开始
要使用浏览器语音识别,第一步就是创建 SpeechRecognition 实例。不过要注意,这个对象在浏览器里是带前缀的。
// 兼容写法,我习惯这样写
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
console.error('浏览器不支持语音识别');
return;
}
const recognition = new SpeechRecognition();
嗯,这里有个坑。Safari 和 Firefox 虽然也实现了部分 Web Speech API,但 SpeechRecognition 目前还是 Chrome 系浏览器支持最好。我在项目中遇到过用户用 Safari 打开后完全没反应的情况,所以建议在入口处做特性检测,并给出友好提示。
3.2 连续识别 vs 单次识别:两种模式怎么选?
这是最核心的配置项之一。先看代码:
// 单次识别模式(默认)
recognition.continuous = false;
// 连续识别模式
recognition.continuous = true;
两种模式的区别,我用一个表格来说明:
| 特性 | 单次识别(continuous: false) | 连续识别(continuous: true) |
|---|---|---|
| 触发方式 | 用户停止说话后自动结束 | 持续监听,直到手动停止 |
| 适用场景 | 搜索框、语音指令、短命令 | 语音输入法、会议记录、实时字幕 |
| 资源消耗 | 低,用完即释放 | 较高,需要持续占用麦克风 |
| 结果回调 | 一次性返回最终结果 | 多次返回中间结果和最终结果 |
| 自动重启 | 不需要 | 通常需要监听 end 事件手动重启 |
我的建议: 如果你做的是语音助手类的产品,比如“打开设置”、“下一页”这种短指令,用单次模式就够了。如果是做语音输入法或者会议记录,必须用连续模式。我曾经在一个项目里误用了单次模式做语音输入,结果用户说长句时识别到一半就断了,体验非常糟糕。
3.3 语言设置:让 API 听懂你的方言
语言设置通过 lang 属性控制。这个属性决定了语音识别引擎用哪种语言模型来解析音频。
// 设置中文普通话
recognition.lang = 'zh-CN';
// 设置美式英语
recognition.lang = 'en-US';
// 设置粤语
recognition.lang = 'zh-HK';
// 设置日语
recognition.lang = 'ja-JP';
常见的语言代码对照表:
| 语言 | 语言代码 | 备注 |
|---|---|---|
| 中文普通话 | zh-CN | 最常用,识别率较高 |
| 中文(台湾) | zh-TW | 繁体字,口音不同 |
| 粤语 | zh-HK | 香港地区使用 |
| 美式英语 | en-US | 全球通用 |
| 英式英语 | en-GB | 英式发音 |
| 日语 | ja-JP | 支持较好 |
| 韩语 | ko-KR | 支持一般 |
小技巧: 如果你需要支持多语言,可以在用户选择语言时动态设置 recognition.lang。但要注意,切换语言后最好重新创建 Recognition 实例,因为有些浏览器在运行时修改 lang 属性不会生效。我踩过这个坑,后来统一采用“重新实例化”的策略。
3.4 结果回调:拿到识别文本的关键
语音识别的结果通过事件回调返回。最常用的是 result 事件。
recognition.onresult = function(event) {
// event.results 是一个 SpeechRecognitionResultList 对象
const result = event.results[event.results.length - 1];
// result[0] 是最佳识别结果
const transcript = result[0].transcript;
const confidence = result[0].confidence;
console.log('识别文本:', transcript);
console.log('置信度:', confidence);
// 判断是否是最终结果
if (result.isFinal) {
console.log('这是最终结果');
} else {
console.log('这是中间结果');
}
};
这里有几个关键点需要理解:
- event.results:是一个类数组对象,每个元素对应一次识别的结果。连续模式下,每次语音输入都会追加一条。
- result[0]:每个识别结果包含多个候选,
result[0]是置信度最高的那个。 - transcript:识别出的文本内容。
- confidence:置信度,0 到 1 之间的小数。越高说明引擎越有把握。
- isFinal:标记当前结果是中间结果还是最终结果。连续模式下,用户说话过程中会不断返回中间结果,说话停顿后返回最终结果。
注意: 置信度并不总是可靠的。我在项目中遇到过置信度 0.95 但识别完全错误的情况,也遇到过置信度 0.3 但识别正确的情况。所以不要完全依赖置信度做业务判断,尤其是关键指令场景。
3.5 其他重要事件:监听整个生命周期
除了 result,还有几个事件需要关注:
// 开始监听
recognition.onstart = function() {
console.log('语音识别已启动,请说话...');
// 可以在这里更新 UI,比如显示麦克风图标动画
};
// 监听结束
recognition.onend = function() {
console.log('语音识别已结束');
// 连续模式下,需要在这里重启
if (recognition.continuous) {
recognition.start();
}
};
// 发生错误
recognition.onerror = function(event) {
console.error('识别出错:', event.error);
// 常见错误:no-speech(没检测到语音)、aborted(被中断)、network(网络问题)
switch (event.error) {
case 'no-speech':
// 提示用户说话
break;
case 'aborted':
// 用户手动停止
break;
case 'network':
// 检查网络连接
break;
default:
// 其他错误
}
};
// 音频开始捕获
recognition.onaudiostart = function() {
console.log('开始捕获音频');
};
// 音频结束捕获
recognition.onaudioend = function() {
console.log('音频捕获结束');
};
你想想看,如果连续模式下不监听 end 事件并重启,识别就会在用户第一次说完话后自动停止。这是新手最容易犯的错误。
3.6 完整示例:一个可用的语音识别组件
把上面的知识点串起来,写一个完整的示例:
class VoiceRecognition {
constructor(options = {}) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
throw new Error('浏览器不支持语音识别');
}
this.recognition = new SpeechRecognition();
this.recognition.lang = options.lang || 'zh-CN';
this.recognition.continuous = options.continuous || false;
this.recognition.interimResults = options.interimResults || false;
this.isListening = false;
this.setupCallbacks(options);
}
setupCallbacks(options) {
this.recognition.onresult = (event) => {
const result = event.results[event.results.length - 1];
const transcript = result[0].transcript;
const confidence = result[0].confidence;
if (result.isFinal) {
options.onFinalResult?.(transcript, confidence);
} else {
options.onInterimResult?.(transcript, confidence);
}
};
this.recognition.onerror = (event) => {
options.onError?.(event.error);
};
this.recognition.onend = () => {
this.isListening = false;
options.onEnd?.();
// 连续模式下自动重启
if (this.recognition.continuous && !this._stopped) {
this.start();
}
};
}
start() {
this._stopped = false;
this.recognition.start();
this.isListening = true;
}
stop() {
this._stopped = true;
this.recognition.stop();
this.isListening = false;
}
abort() {
this._stopped = true;
this.recognition.abort();
this.isListening = false;
}
}
// 使用示例
const recognizer = new VoiceRecognition({
lang: 'zh-CN',
continuous: true,
interimResults: true,
onFinalResult: (text, confidence) => {
console.log('最终结果:', text, '置信度:', confidence);
},
onInterimResult: (text, confidence) => {
console.log('中间结果:', text);
},
onError: (error) => {
console.error('错误:', error);
},
onEnd: () => {
console.log('识别结束');
}
});
recognizer.start();
3.7 核心流程一览
为了让你更直观地理解整个语音识别的工作流程,我画了一张图:
这张图把整个流程串起来了。从创建实例到配置参数,再到启动识别和事件处理,最后拿到结果。你写代码的时候可以对照着这张图,不容易漏掉关键步骤。
3.8 避坑指南:我踩过的那些坑
最后分享几个实战中容易遇到的问题:
- HTTPS 要求:语音识别 API 要求在 HTTPS 环境下运行。本地开发可以用 localhost,但线上必须是 HTTPS。我曾经在 HTTP 环境下调试了半天,一直报错,后来才发现是协议问题。
- 用户授权:首次调用
start()时,浏览器会弹出麦克风权限请求。如果用户拒绝,会触发error事件,错误类型是not-allowed。建议在调用前先检查权限状态。 - 内存泄漏:连续模式下,如果频繁创建和销毁 Recognition 实例,可能会导致内存泄漏。我习惯用一个全局实例,通过
start()和stop()控制开关,而不是每次都 new 一个新的。 - 音频冲突:如果页面中同时有多个 Recognition 实例在运行,可能会产生音频冲突。确保同一时间只有一个实例在监听。
核心要点回顾:
- 创建实例时注意浏览器前缀兼容
- 根据场景选择单次或连续模式
- 语言设置要匹配用户实际使用的语言
- 结果回调中区分中间结果和最终结果
- 连续模式下必须在 onend 中重启
- 做好错误处理和权限管理