根本原因是语音引擎异步加载且需用户交互触发:①getvoices()初始为空,须监听onvoiceschanged或轮询就绪;②必须显式设lang='zh-cn'防fallback英文;③每次speak()前需cancel()清队列并新建utterance实例。

直接用 window.speechSynthesis 就能实现,但不处理语音就绪时机、不绑定用户点击、不显式设 lang,90% 的页面会静音——不是代码错了,是浏览器根本没让它播。
speechSynthesis.speak() 为什么一调用就没声音
最常见原因是:在页面加载完成(DOMContentLoaded)或定时器里直接调用 speak(),而此时 speechSynthesis.getVoices() 还返回空数组。语音引擎加载是异步的,且 iOS/Safari 还要求用户先点过页面才开始加载。
- 必须监听
speechSynthesis.onvoiceschanged,或轮询getVoices().length > 0才算就绪 - Chrome/Edge 通常 100–300ms 就 ready;Safari macOS 可能要等用户首次点击后才触发该事件
- 别在
setTimeout(() => speak(), 1000)里硬等——移动端可能永远等不到
中文朗读总像机器人或念英文
核心问题出在 SpeechSynthesisUtterance.lang 没设,或设得不标准。Chrome 会 fallback 到系统默认语音(常是 en-US),导致“你好”读成 /hɛˈloʊ/。
- 必须显式写
utterance.lang = 'zh-CN',不能只写'zh'或'chinese' -
utterance.rate = 0.9(范围 0.1–10):中文建议 0.8–1.1,>1.2 容易吞字 -
utterance.pitch = 1(0–2):过高发尖,过低沉闷,中文默认值即可 - 音量慎设
volume = 0:某些安卓 WebView 下仍漏底噪
点击按钮后多次触发,语音越堆越多
speechSynthesis.speak() 不会覆盖前一个,而是排队执行。连点三次“朗读”,就得听三遍——除非你主动清理队列。
- 每次调用前必须先
speechSynthesis.cancel(),清空所有待播和正在播的内容 -
utterance实例不能复用:每次都要new SpeechSynthesisUtterance(text) -
pause()和resume()只对当前正在播的生效;若已播完,resume()无效 - 不要依赖
speechSynthesis.pending判断是否在播放——它只表示队列非空,不反映真实发声状态
iOS/Safari 和微信 X5 内核的硬限制
这些环境根本不允许“自动播放语音”,哪怕你绑了 click,如果调用 speak() 发生在异步回调里(比如 Promise.then、setTimeout、fetch 回调),依然会被拒绝并抛 DOMException: The user gesture requirement is not met。
- 必须在原生用户事件处理器内同步调用:
button.addEventListener('click', () => { ... speechSynthesis.speak(utterance); }) - 微信 X5 内核默认禁用 Web Speech API,检测到
!'speechSynthesis' in window就得降级(比如显示文字高亮+播放图标) - HTTPS 是硬性前提:HTTP 站点下
window.speechSynthesis直接为undefined
真正容易被忽略的,是语音列表加载完成之后,还得手动匹配可用 voice:有些设备返回的 zh-CN voice 实际不可用,utterance.voice 设为 null 才会走系统默认,设错反而静音。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











