☰
在 airi 项目中用 VueUse useSpeechSynthesis 实现响应式语音朗读
2026/10/13 9:26:06 网站建设 项目流程

在 airi 项目中用 VueUse useSpeechSynthesis 实现响应式语音朗读

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

useSpeechSynthesis是 VueUse 对 Web Speech Synthesis API(SpeechSynthesis)的响应式封装,属于 Sensors 分类下的组合式函数。它把浏览器朗读能力抽象为isSupported、isPlaying、status、utterance等响应式状态,并暴露speak()、toggle()、stop()等控制方法,让你在 Vue 3 / Nuxt 3 组件里用几行代码即可完成"文本转语音"的完整交互。阅读本文后,你将掌握该组合式函数的所有参数、返回值与类型约束,并能直接把它接入 airi 仓库中的任意 Vue 应用(如 apps/stage-web、apps/stage-tamagotchi)实现 TTS 朗读能力。

本文主体来源于仓库内文档 .agents/skills/vueuse-functions/references/useSpeechSynthesis.md,并辅以 .agents/skills/vueuse-functions/SKILL.md 中关于函数选用规则(Invocation 规则)的说明展开。

为什么需要响应式封装

Web Speech Synthesis API 本身是命令式的:你需要手动创建SpeechSynthesisUtterance实例、监听start/end/pause/resume/boundary/error等一系列事件,再手动维护"当前是否在朗读"、"当前状态"等标志位,极易产生样板代码与状态不同步问题。

useSpeechSynthesis将这些细节全部收敛:

  • 自动检测window.speechSynthesis是否存在(SSR 场景下同样安全),通过isSupported暴露支持情况;
  • 内部创建并持有SpeechSynthesisUtterance,通过utterance以ComputedRef形式暴露;
  • 将朗读状态收敛为status("init" | "play" | "pause" | "end")与isPlaying两个响应式信号;
  • 提供speak()、toggle()、stop()三个控制函数,组件内无需再与底层 API 直接打交道。

在 .agents/skills/vueuse-functions/SKILL.md 中,useSpeechSynthesis被归入 Sensors 类别,Invocation 规则为AUTO——即"在适用场景下应自动优先使用",而非要求显式请求或额外安装依赖。这与该 skill 的核心主张一致:优先用 VueUse 组合式函数替代手写逻辑,以保持代码简洁、可维护且性能良好。

基础用法

从@vueuse/core引入并在组件中调用:

import { useSpeechSynthesis } from '@vueuse/core' const { isSupported, isPlaying, status, voiceInfo, utterance, error, stop, toggle, speak, } = useSpeechSynthesis()

调用后即可直接使用这些返回值。一个最小可用的 Vue 组件示例如下:

<script setup lang="ts"> import { ref } from 'vue' import { useSpeechSynthesis } from '@vueuse/core' const text = ref('你好,我是 A.I.R.I,很高兴认识你。') const { isSupported, isPlaying, status, speak, stop, toggle } = useSpeechSynthesis(text) </script> <template> <div v-if="isSupported"> <textarea v-model="text" /> <button @click="speak()">朗读</button> <button @click="toggle()">{{ isPlaying ? '暂停' : '继续' }}</button> <button @click="stop()">停止</button> <span>当前状态:{{ status }}</span> </div> <p v-else>当前环境不支持 Speech Synthesis API</p> </template>

注意useSpeechSynthesis的第一个参数是文本内容,类型为MaybeRefOrGetter<string>,即可以直接传字符串、ref或 getter 函数。传入ref时,朗读文本会随响应式数据自动更新,这正是"响应式 SpeechSynthesis"的含义。

Options:语音参数与默认值

useSpeechSynthesis(text, options)的第二个参数UseSpeechSynthesisOptions直接透传给底层的SpeechSynthesisUtterance,默认值如下:

import { useSpeechSynthesis } from '@vueuse/core' useSpeechSynthesis('朗读内容', { lang: 'en-US', // 语言,默认 en-US pitch: 1, // 音高,默认 1 rate: 1, // 语速,默认 1 volume: 1, // 音量,默认 1 })

各选项的作用与取值范围说明如下:

选项类型默认值说明
langMaybeRefOrGetter<string>'en-US'朗读使用的语言(BCP 47 语言标签,如'zh-CN'、'ja-JP')。应与所选voice匹配,否则可能使用系统默认语音
pitchMaybeRefOrGetter<SpeechSynthesisUtterance["pitch"]>1音高,数值范围 0~2(典型取值 0~2,越接近 2 声音越尖)
rateMaybeRefOrGetter<SpeechSynthesisUtterance["rate"]>1语速,数值范围 0.1~10(1 为正常语速)
voiceMaybeRef<SpeechSynthesisVoice>无默认值指定使用的语音对象,可通过speechSynthesis.getVoices()获取候选列表,不传则由系统选择
volumeMaybeRefOrGetter<SpeechSynthesisUtterance["volume"]>1音量,数值范围 0~1
onBoundary(event: SpeechSynthesisEvent) => void无触发boundary事件时的回调,可用于实现逐词高亮、字幕同步等效果

关键点:

  • lang、pitch、rate、volume均接受MaybeRefOrGetter,意味着这些参数可以绑定到响应式数据上,用户在界面上拖动滑块调节语速、音高时,下一次speak()自动使用新值;
  • voice只接受MaybeRef(ref 或普通值),因为语音对象是浏览器提供的实例,不适合用 getter 动态推导;
  • 由于SpeechSynthesisUtterance的属性(如pitch、rate)在不同浏览器上校验严格程度不同,超出范围的值可能被钳制或忽略,建议在 UI 层做好范围约束。

返回值详解

useSpeechSynthesis返回UseSpeechSynthesisReturn,字段说明如下:

返回值类型说明
isSupportedboolean(来自Supportable)当前环境是否支持 Speech Synthesis API,用于条件渲染或功能降级
isPlayingShallowRef<boolean>当前是否处于播放状态
statusShallowRef<UseSpeechSynthesisStatus>朗读状态机:"init" \| "play" \| "pause" \| "end"
voiceInfo语音信息当前使用的语音信息(用于展示或调试)
utteranceComputedRef<SpeechSynthesisUtterance>当前朗读的SpeechSynthesisUtterance实例,文本或参数变化时会重新生成
errorShallowRef<SpeechSynthesisErrorEvent \| undefined>最近一次朗读错误事件,无错误时为undefined
stop() => void立即停止朗读
toggle(value?: boolean) => void切换播放 / 暂停;传入value时可强制指定目标状态
speak() => void使用当前文本与参数开始朗读

典型的使用模式:

const { isPlaying, status, error, stop, toggle, speak } = useSpeechSynthesis(text) // 在朗读结束时做清理或触发下一步 watch(status, (s) => { if (s === 'end') console.log('朗读结束') }) // 朗读出错时回退到文本提示 watch(error, (e) => { if (e) console.error('语音朗读失败', e) })

toggle()不带参数时自动切换播放与暂停:播放中调用则暂停,暂停中调用则恢复。这在"语音播放/暂停"按钮的实现中尤其好用,无需手动判断当前状态。

类型声明:一次看懂完整接口

文档给出了完整的 TypeScript 类型声明,它是理解该函数能力边界的最佳入口:

export type UseSpeechSynthesisStatus = "init" | "play" | "pause" | "end" export interface UseSpeechSynthesisOptions extends ConfigurableWindow { lang?: MaybeRefOrGetter<string> pitch?: MaybeRefOrGetter<SpeechSynthesisUtterance["pitch"]> rate?: MaybeRefOrGetter<SpeechSynthesisUtterance["rate"]> voice?: MaybeRef<SpeechSynthesisVoice> volume?: MaybeRefOrGetter<SpeechSynthesisUtterance["volume"]> onBoundary?: (event: SpeechSynthesisEvent) => void } export interface UseSpeechSynthesisReturn extends Supportable { isPlaying: ShallowRef<boolean> status: ShallowRef<UseSpeechSynthesisStatus> utterance: ComputedRef<SpeechSynthesisUtterance> error: ShallowRef<SpeechSynthesisErrorEvent | undefined> stop: () => void toggle: (value?: boolean) => void speak: () => void } export declare function useSpeechSynthesis( text: MaybeRefOrGetter<string>, options?: UseSpeechSynthesisOptions, ): UseSpeechSynthesisReturn

值得注意的类型细节:

  • UseSpeechSynthesisStatus是一个四态字符串联合类型,"init"表示尚未开始,"play"/"pause"/"end"对应朗读的三种终态与中间态,用status === 'end'即可可靠判断一轮朗读是否结束;
  • UseSpeechSynthesisOptions extends ConfigurableWindow:与 VueUse 中大多数浏览器相关组合式函数一致,允许通过window选项传入自定义窗口对象(主要用于测试或特殊宿主环境,如 Electron 渲染进程);
  • 返回值extends Supportable:保证isSupported字段的存在,且isSupported在 SSR 环境下为false,避免服务端渲染时误调用浏览器 API;
  • utterance是ComputedRef,当text或lang/pitch/rate/volume等响应式参数变化时,内部会重新构建SpeechSynthesisUtterance实例,保证每次speak()都使用最新配置。

在 airi 仓库中的接入实践

airi 仓库是一个多应用、多包的大型 Vue 3 工程,多个应用与包都声明了对@vueuse/core的依赖,包括 apps/stage-web/package.json、apps/stage-tamagotchi/package.json、apps/stage-pocket/package.json、packages/stage-ui/package.json、packages/stage-shared/package.json 等,因此useSpeechSynthesis可直接在这些工程中开箱即用,无需额外安装依赖(这与 SKILL.md 中该函数的AUTO调用规则一致)。

一个贴近实际场景的完整示例——带语速 / 音量控制与状态展示的朗读面板:

<script setup lang="ts"> import { ref } from 'vue' import { useSpeechSynthesis } from '@vueuse/core' const message = ref('A.I.R.I 正在为你朗读这条消息。') const rate = ref(1) const volume = ref(1) const { isSupported, isPlaying, status, error, speak, toggle, stop } = useSpeechSynthesis(message, { lang: 'zh-CN', rate, volume, }) </script> <template> <section v-if="isSupported"> <textarea v-model="message" rows="2" /> <label>语速 <input v-model.number="rate" type="range" min="0.5" max="2" step="0.1" /></label> <label>音量 <input v-model.number="volume" type="range" min="0" max="1" step="0.1" /></label> <button @click="speak()">开始朗读</button> <button @click="toggle()">{{ isPlaying ? '暂停' : '继续' }}</button> <button @click="stop()">停止</button> <p>状态:{{ status }}<span v-if="error">(错误:{{ error.error }})</span></p> </section> </template>

注意事项与限制

  1. 浏览器支持差异:Speech Synthesis API 的支持程度与语音质量因浏览器、操作系统而异(可通过isSupported检测基本可用性)。部分浏览器要求页面交互后才能触发朗读,首次调用speak()前建议先由用户手势触发。在 Chromium 系浏览器上,还存在已知的长时间朗读后静默停止的问题,可监听status变为'end'但实际未播完的情况做兜底。
  2. SSR 安全:由于继承Supportable且基于window能力检测,isSupported在服务端渲染时为false,配合v-if="isSupported"可以避免在 SSR 阶段调用浏览器 API。
  3. 语音与语言匹配:lang只声明语言,实际使用哪个语音由系统决定;若需要精确控制,请通过speechSynthesis.getVoices()获取SpeechSynthesisVoice列表,用voice选项显式指定。
  4. 范围校验:pitch(0~2)、rate(0.1~10)、volume(0~1)超出浏览器合法范围时行为不确定,建议在 UI 上限制滑块范围。
  5. 语音切换异步性:getVoices()在部分浏览器上首次调用可能返回空数组,需要在voiceschanged事件后再读取,接入时注意时序。

小结

useSpeechSynthesis以最小的 API 面覆盖了 Web Speech Synthesis 的核心能力:isSupported做能力检测、status/isPlaying做状态追踪、speak()/toggle()/stop()做播放控制,配合lang/pitch/rate/volume等响应式参数,可以在 Vue 3 应用中快速构建出健壮的 TTS 朗读功能。对于 airi 这类大量使用 Vue 3 与@vueuse/core的工程,它正是 SKILL.md 所倡导的"用组合式函数替代手写逻辑"的典型实践。若需要完整的类型约束与参数语义,可直接查阅参考文档 useSpeechSynthesis.md。

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询