使用 window.speechSynthesis 实现语音播放需解决三大问题:语音列表未就绪(须监听 voiceschanged 事件并兜底轮询)、首次播放未由用户手势触发(导致静默拒绝)、中文语音未正确设置(lang='zh-CN'并关键词匹配);同时需每次新建 utterance、点击前 cancel() 防堆积、长文本分句处理,并针对 iOS/Safari/微信 X5 做交互与语速适配。

直接用 window.speechSynthesis 就能实现,但多数“没声音”不是代码写错,而是被浏览器策略拦住或语音引擎根本没就绪。
为什么点了按钮没反应?
静默失败是常态,几乎都不报错,原因集中在三处:
-
speechSynthesis.getVoices()初始返回空数组,尤其 Safari 和 iOS WebView 必须等voiceschanged事件触发后才能拿到可用语音 - 页面未通过用户手势(
click或touchend)触发首次speak(),Chrome/Safari 会直接拒绝,报DOMException: The user gesture requirement is not met - 没显式设置
utterance.lang = 'zh-CN',导致 Chrome 用英文语音读中文,吐字含糊甚至跳过整段
如何安全初始化并选中中文语音?
不能在 DOMContentLoaded 里直接调用 getVoices() 就开播,得等语音列表真正加载完成:
- 监听
speechSynthesis.onvoiceschanged,首次触发后再执行语音选择逻辑 - 匹配语音时优先用
v.lang === 'zh-CN', fallback 到v.name.includes('Chinese') || v.name.includes('Yaoyao')等关键词,避免依赖索引取voices[0] - 每次播放前都新建
SpeechSynthesisUtterance实例,不可复用旧对象 - 建议封装一个
initSpeech()函数,在页面加载后注册onvoiceschanged,并设个 2s 轮询兜底(Safari 首次交互后才加载语音)
怎么控制播放、暂停和防堆积?
speechSynthesis 是单通道队列,不处理并发,容易点多次就卡住或中断前一个:
把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。
立即学习“前端免费学习笔记(深入)”;
- 点击朗读按钮前必须先调用
speechSynthesis.cancel(),清空待播队列,否则快速连点会堆积 -
pause()和resume()只对当前正在播的utterance有效;若已播完,resume()无效 - 要“停止当前、不播下一个”,不能只
cancel()—— 它会清空整个队列。正确做法是:先pause(),再cancel() - 长文本(如整段文章)务必按句号/问号/感叹号切分,每段新建
utterance并链式调用,避免单次过长导致中断丢失
移动端和 Safari 特别要注意什么?
微信 X5 内核默认禁用,iOS Safari 对交互要求最严,很多细节不处理就会静音:
- 首次
speak()必须由用户真实点击/触摸同步触发,不能包在setTimeout、Promise.then或fetch回调里 - 中文标点必须保留,比如“100%”不能替换成“百分之一百”,否则 TTS 会误读
- 语速建议设为
0.9–1.1,设成1.3以上在安卓部分 WebView 上大概率吞字 - 不支持时不要硬撑,降级方案优先做视觉反馈(如高亮文字)或复制到剪贴板,比放个哑巴喇叭强
真正难的不是调用 speak(),而是让语音引擎在各种设备上稳定就绪、让中文不读成英文腔、让用户点下去真有声——这些全靠状态监听、兜底轮询和细粒度控制,不是写几行代码就能跑通的。


















