日常开发中,很多后台系统、智能客服、AI 对话类项目,都会用到 文字转语音(TTS) 和 语音转文字(STT) 功能。
很多同学第一反应是引入第三方付费 SDK,其实浏览器原生就提供了成熟的 Web Speech API,零成本、无需后端、开箱即用,完美满足大部分业务场景。
本文基于 Vue2/Vue3 通用语法,手把手实现双向语音功能,包含:核心原理、完整代码、启停控制、异常处理、兼容适配、常见坑点,直接复制即可投入项目使用。
一、核心技术前置了解
Web Speech API 是浏览器原生语音接口,主要分为两大模块,对应我们的两个核心功能:
1. 文字转语音:SpeechSynthesis
将文本内容转换为语音朗读,属于浏览器本地合成,无需联网、无接口请求,响应速度极快。
支持配置:朗读语速、音调、音量、选择人声、启停/暂停/继续等操作。
2. 语音转文字:SpeechRecognition
实时监听麦克风音频,将语音实时识别为文字,依赖浏览器语音识别服务,需要联网 + HTTPS 环境(localhost 本地开发豁免)。
支持配置:识别语言、实时返回片段、结束返回完整文本、监听识别状态等。
✅ 兼容性说明
- 文字转语音:全平台主流浏览器完美兼容
- 语音转文字:Chrome、Edge 内核浏览器支持最佳,Safari 部分版本兼容较差,移动端微信浏览器不支持
二、整体功能实现思路
- 封装原生语音接口,解决浏览器前缀兼容问题
- 实现文字转语音:输入文本 → 配置参数 → 触发朗读 → 支持暂停/停止
- 实现语音转文字:调用麦克风权限 → 实时监听语音 → 输出识别文本
- 增加状态管理:加载中、识别中、朗读中、无权限等状态提示
- 异常捕获:权限拒绝、浏览器不支持、网络异常、识别超时
三、完整代码实现(Vue 通用)
组件代码不区分 Vue2/Vue3,仅组合式/选项式写法可按需微调,核心逻辑完全通用。
xml
<template>
<div class="speech-box">
<h3>Vue 语音双向转换功能演示</h3>
<!-- 文字转语音区域 -->
<div class="tts-box">
<p>【文字转语音】输入需要朗读的内容</p>
<textarea v-model="ttsText" placeholder="请输入朗读文本"></textarea>
<div class="btn-group">
<button @click="startSpeak" :disabled="speaking">开始朗读</button>
<button @click="stopSpeak" :disabled="!speaking">停止朗读</button>
</div>
</div>
<!-- 语音转文字区域 -->
<div class="stt-box">
<p>【语音转文字】点击开始识别,对着麦克风说话</p>
<div class="result-text">识别结果:{{ sttResult || '暂无内容' }}</div>
<div class="btn-group">
<button @click="startRecognition" :disabled="recognitioning">开始识别</button>
<button @click="stopRecognition" :disabled="!recognitioning">结束识别</button>
<button @click="clearResult">清空结果</button>
</div>
<p class="tip-text">{{ statusText }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'SpeechTranslate',
data() {
return {
// 文字转语音
ttsText: '大家好,这是Vue原生文字转语音功能演示',
speaking: false, // 是否正在朗读
speech: null, // 语音实例
// 语音转文字
sttResult: '', // 识别结果
recognitioning: false, // 是否正在识别
recognition: null, // 识别实例
statusText: '等待操作...'
}
},
mounted() {
// 初始化语音实例
this.initSpeech()
// 初始化识别实例
this.initRecognition()
},
beforeDestroy() {
// 组件销毁时终止所有语音进程,防止内存泄漏
this.stopSpeak()
this.stopRecognition()
},
methods: {
// ===================== 文字转语音核心方法 =====================
initSpeech() {
if (!window.speechSynthesis) {
this.$message && this.$message.error('当前浏览器不支持文字转语音功能')
return false
}
return true
},
startSpeak() {
if (!this.initSpeech() || !this.ttsText.trim()) {
this.$message && this.$message.warning('请输入朗读文本')
return
}
// 先停止上一次朗读
window.speechSynthesis.cancel()
// 创建朗读实例
this.speech = new SpeechSynthesisUtterance()
this.speech.text = this.ttsText // 朗读文本
this.speech.lang = 'zh-CN' // 中文朗读
this.speech.rate = 1 // 语速 0.1-10
this.speech.pitch = 1 // 音调 0-2
this.speech.volume = 1 // 音量 0-1
// 朗读开始
this.speech.onstart = () => {
this.speaking = true
}
// 朗读结束
this.speech.onend = () => {
this.speaking = false
}
// 朗读报错
this.speech.onerror = () => {
this.speaking = false
this.$message && this.$message.error('语音朗读失败')
}
// 执行朗读
window.speechSynthesis.speak(this.speech)
},
stopSpeak() {
window.speechSynthesis.cancel()
this.speaking = false
},
// ===================== 语音转文字核心方法 =====================
initRecognition() {
// 兼容不同浏览器前缀
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition
if (!window.SpeechRecognition) {
this.statusText = '当前浏览器不支持语音识别'
return false
}
// 创建识别实例
this.recognition = new SpeechRecognition()
this.recognition.lang = 'zh-CN' // 识别中文
this.recognition.continuous = true // 连续识别
this.recognition.interimResults = true // 返回临时识别结果
// 识别结果回调
this.recognition.onresult = (e) => {
let result = ''
// 遍历识别片段
for (let i = e.resultIndex; i < e.results.length; i++) {
result += e.results[i][0].transcript
}
this.sttResult = result
}
// 识别开始
this.recognition.onstart = () => {
this.recognitioning = true
this.statusText = '正在聆听,请说话...'
}
// 识别结束
this.recognition.onend = () => {
// 持续开启识别
if (this.recognitioning) {
this.recognition.start()
} else {
this.statusText = '识别已结束'
}
}
// 识别报错
this.recognition.onerror = (err) => {
this.recognitioning = false
this.statusText = `识别失败:${err.error}`
if (err.error === 'not-allowed') {
this.$message && this.$message.error('麦克风权限被拒绝,请手动开启权限')
}
}
return true
},
startRecognition() {
if (!this.recognition) {
if (!this.initRecognition()) return
}
this.recognition.start()
},
stopRecognition() {
if (this.recognition) {
this.recognition.stop()
}
this.recognitioning = false
this.statusText = '已停止识别'
},
clearResult() {
this.sttResult = ''
this.statusText = '等待操作...'
}
}
}
</script>
<style scoped>
.speech-box {
width: 90%;
max-width: 600px;
margin: 30px auto;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
.tts-box, .stt-box {
margin: 20px 0;
padding: 20px;
background: #f8f9fa;
border-radius: 6px;
}
textarea {
width: 100%;
height: 80px;
padding: 10px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
}
.btn-group {
margin-top: 10px;
}
button {
padding: 6px 16px;
margin-right: 10px;
border: none;
border-radius: 4px;
background: #409eff;
color: #fff;
cursor: pointer;
}
button:disabled {
background: #99bcf7;
cursor: not-allowed;
}
.result-text {
padding: 10px;
background: #fff;
border-radius: 4px;
margin: 10px 0;
min-height: 20px;
}
.tip-text {
color: #666;
font-size: 14px;
margin: 5px 0 0;
}
</style>
四、核心参数详解(可自定义配置)
1. 文字转语音参数
- lang:朗读语言,zh-CN 中文、en-US 英文
- rate:朗读语速,取值 0.1 ~ 10,默认 1
- pitch:音调,取值 0 ~ 2,默认 1,数值越高音调越尖锐
- volume:音量,取值 0 ~ 1,默认 1
2. 语音转文字参数
- lang:识别语言,支持中英文切换
- continuous:是否连续识别,true 持续监听,false 单次识别
- interimResults:是否返回临时结果,开启后可实时展示说话内容,不用等结束
五、项目常见坑点 & 解决方案
坑点 1:线上环境功能失效,本地正常
原因 :语音识别功能 必须要求 HTTPS 协议,HTTP 明文协议会直接禁用麦克风权限。
解决:线上项目部署 HTTPS 证书,本地 localhost、127.0.0.1 属于浏览器豁免地址,可正常使用。
坑点 2:多次点击朗读,语音重叠播放
解决 :每次朗读前执行 window.speechSynthesis.cancel(),终止上一次语音进程,避免重叠。
坑点 3:麦克风权限拒绝后无法再次触发
解决 :捕获 not-allowed 错误,提示用户手动在浏览器地址栏开启麦克风权限。
坑点 4:组件销毁后,语音还在后台播放
解决 :在 beforeDestroy 生命周期中强制终止朗读和识别,防止内存泄漏和后台运行。
坑点 5:部分浏览器不支持
解决:提前做兼容性判断,不支持时给出友好提示,不阻塞页面功能。
六、优缺点总结
✅ 优点
- 原生 API、零依赖、无需安装第三方包、轻量化
- 文字转语音本地执行,无接口费用、无请求延迟
- 开发成本极低,十分钟即可接入完整功能
❌ 缺点
- 语音转文字依赖浏览器内核,部分小众浏览器不兼容
- 识别准确率依赖浏览器官方语音服务,无法自定义模型
- 纯前端实现,无法私有化部署,高精准场景需对接第三方 AI SDK
七、拓展升级方向
如果业务需要更高精度、私有化部署、离线识别,可以基于本文基础上升级:
- 对接 百度AI、阿里云、讯飞语音 SDK,提升识别准确率
- 增加语音暂停、继续、分段朗读功能
- 记录历史识别记录,本地缓存保存
- 适配移动端,优化触摸点击体验
- 增加多语言切换、方言识别功能
最后
浏览器原生 Web Speech API 完全可以满足绝大多数 ToB 后台、简单 AI 对话、辅助输入场景的语音需求,相比第三方 SDK,具备零成本、轻量、快速上线的绝对优势。
本文代码开箱即用,复制到 Vue 项目即可直接运行,无需额外配置,希望能帮到有需要的同学!