简介面向React开发者与初学者的语音转文字应用源码基于Create React App脚手架构建利用浏览器内置语音识别API将语音实时转为文字适合语音输入、语音笔记、会议记录等场景的功能参考与二次开发基础。资源包共17个文件以JavaScript逻辑文件为主5个js含组件与入口逻辑辅以CSS样式、JSON配置、Markdown说明文档等整体压缩包仅117KB目录结构清晰便于按功能模块快速定位源码与配置。当前已有672人学习/下载源码包含App与Recognition组件覆盖了界面渲染、语音识别触发、结果处理等核心流程并附有初始化脚手架配置文件与说明文档。读者可据此学习React组件拆分方法、状态管理思路以及如何在浏览器端调用Web Speech API实现实时语音转文字还能参考配置自定义主题、代码分割、环境变量等工程化细节适合作为个人练习或轻量级语音工具的原型起点。1. 一个能直接跑起来的语音转文字 React 应用为什么值得下载如果你正在找把语音转成文字的现成前端方案这个基于 React 的 voice-to-speech 项目值得你花一下午跑一遍。它没有后端服务也没有离线模型全部转录能力都来自内置的浏览器 API——也就是 Web Speech API 的 SpeechRecognition 接口。换句话说只要能打开一个支持该接口的浏览器你就能获得一套可复用的语音识别前端链路。我第一次跑通这个项目时最惊讶的是浏览器把语音识别的细节拆得这么清楚语音开始、结束、中间结果、最终结果都是独立事件。你不需要自己处理音频采集、采样率转换、端点检测这些复杂问题只要监听事件把返回的文字流进 React 状态就行。项目把这条链路封装成了几十行的 hook 和组件适合用来做会议速记、采访整理、视频字幕生成甚至只是给应用加一个「说话输入」的交互入口。对于刚接触前端语音识别的人它是一个很好的逆向学习样本能让你看清楚浏览器原生 API 的边界在哪。对于做过后端识别服务的开发者它也能作为轻量级方案对比参照。我的建议是把项目跑起来用一段带口音的录音试几次你会快速理解为什么浏览器转录的稳定性比专用服务差一截但应急场景下又足够用。2. 项目定位与技术选型为什么挑浏览器原生识别而不是私有模型2.1 项目设计的边界一个纯前端的转录工具这个项目的定位很清楚它不追求识别精度也不打算做离线部署而是把「语音进入浏览器、浏览器返回文字」这条链路用 React 表达清楚。我拆完源码后判断它的目标人群主要有三类。第一类是想在现有项目里快速验证语音输入交互的前端开发毕竟搭一套后端识别服务通常要处理鉴权、并发和账单问题第二类是需要给学生演示 Web API 用法的教学场景浏览器原生接口最少依赖、最好解释第三类是个人知识管理工具的使用者希望在写笔记或速记时解放双手能接受把音频发到服务商的识别接口。因为识别能力由浏览器提供项目自身不包含任何模型权重。这直接拉低了工程复杂度没有服务端路由没有鉴权逻辑也没有跨域配置。开发环境里把项目跑起来在支持的浏览器中打开页面授权麦克风就可以开始转录。项目里有一个值得注意的设计它把「开始收听」和「停止收听」映射成页面按钮的状态同时对连续音频段做时间戳标记为后续导出字幕保留扩展点。2.2 技术栈组成React 与 JavaScript 的黏合方式从源码依赖来看这是一个标准的 Create React App 初始化项目核心逻辑全部用 JavaScript 编写没有 TypeScript 类型噪音。React 负责视图层状态管理语音识别由浏览器 API 提供两者之间通过自定义事件完成通信。我拆项目一般先看 package.json 的 dependencies这里除了 react 和 react-dom 之外没有任何第三方语音库说明语音能力确实来自浏览器原生接口。这种技术选择的好处是可维护性。当 Web Speech API 更新时只需要调整事件处理层而如果引入开源语音识别库则要同时管理模型加载、音频流格式化和推理线程问题。缺点是浏览器兼容性直接决定工具可用性。所以项目里通常会放一段环境检测逻辑检查 window.SpeechRecognition 是否存在不存在就给出引导升级浏览器的提示。用 JavaScript 而不是 TypeScript对这类教学性质的 demo 更友好。少数几个状态类型的可读性不会因为缺了 interface 而崩坏反而让刚入门的人能直接看懂。如果你打算在团队内推广可以自己补类型我后面会在二次开发部分讲具体位置。2.3 目录结构一个单体应用的标准骨架这里给出我拆解后的目录结构省略了 node_modules 和构建产物src/ api/ recognition.js # 封装语音识别实例创建与事件绑定 components/ TranscriptPanel.jsx # 最终转录面板 InterimPanel.jsx # 临时转录面板 ControlBar.jsx # 开始/停止按钮与状态展示 hooks/ useSpeechRecognition.js # 核心 hook管理转录状态 App.jsx index.js public/ index.html这个结构很清爽。api 目录把浏览器 API 的副作用隔离起来hooks 目录面向业务components 目录只负责展示。如果要二次开发重点读 recognition.js 和 useSpeechRecognition.js其他文件基本是 React 样板。项目没有把逻辑全部堆在组件里而是用 hook 抽出这为单元测试留了方便门。你在改动时不要破坏这个分层否则事件绑定和状态更新混在一起会非常难调试。2.4 运行环境要求Chromium 内核的浏览器是硬前提Web Speech API 目前只在 Chromium 内核的浏览器中稳定支持这是项目能跑起来的第一前提。Firefox 和 Safari 虽然实现了部分音频采集能力但 SpeechRecognition 接口并不完整。所以项目在页面初始化时会先跑一次兼容性检查如果失败界面会提示用户更换浏览器。我实测时使用了较新的桌面浏览器版本。开发服务器通过 npm start 启动后页面请求麦克风权限允许后开始拾音。这里有个重要细节浏览器要求 HTTPS 协议才能调用麦克风但 localhost 是例外所以本地开发可以直接用 http。部署到服务器时则必须配置证书否则功能不可用。另外识别过程依赖网络请求语音数据会发送到识别服务所以内网或断网环境下是用不了的。2.5 与第三方识别方案的取舍有人可能会问为什么不直接接云服务商的语音识别 SDK答案在项目设计目标里它要演示的是「零配置」的 Web API 用法。云服务商方案需要注册账号、创建密钥、配置鉴权每次识别还要处理好网络超时和配额。而浏览器内置接口把这些都包装掉了你甚至可以用几行代码完成一次识别。这个取舍带来的是识别质量的下降。浏览器接口在安静环境下、普通话标准时准确率尚可但遇到口音、专业名词、多人说话场景就很吃力。项目里没有声学模型参数可调所以你不能指望它达到专业速记的水平。如果你要做生产级应用我的建议是保留这个项目作为前端原型后端替换成自己训练的模型或商用 API接口层面沿用这里的 hook 结构即可。3. 核心模块拆解从按下按钮到字幕落地的完整链路3.1 recognition 实例的创建与参数配置项目的最底层是 recognition.js负责创建 SpeechRecognition 实例并配置行为。它先做兼容性判断再设定语言、是否返回临时结果、是否持续监听。代码核心部分类似下面这样function createRecognition() { const SpeechRecognition window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { throw new Error(当前浏览器不支持语音识别); } const recognition new SpeechRecognition(); recognition.lang navigator.language || zh-CN; // 优先使用浏览器语言 recognition.interimResults true; // 开启临时结果适合实时字幕 recognition.continuous true; // 连续识别避免一句话后自动停止 return recognition; }这里三个关键参数决定了识别行为。lang控制识别语言中文场景通常用zh-CN如果是英文会场可以改成en-US。interimResults设为 true 后浏览器会频繁回调半成品文字你可以在界面上实时显示代价是结果会有抖动。continuous设为 true 时识别结束后会继续自动收音避免说完一句程序就退出。我在做类似工具时会把lang提成配置项方便用户切换。3.2 事件驱动的转录流onspeechstart、onresult 与 onerrorSpeechRecognition 是事件驱动的项目里通过挂载事件属性来响应不同阶段。核心事件有五个onstart开始服务、onspeechstart检测到语音、onresult返回文字、onerror异常、onend结束服务。转录逻辑主要在 onresult 里完成监听代码大概是recognition.onresult (event) { let interimTranscript ; let finalTranscript ; for (let i event.resultIndex; i event.results.length; i) { const transcript event.results[i][0].transcript; if (event.results[i].isFinal) { finalTranscript transcript; } else { interimTranscript transcript; } } dispatch({ type: UPDATE_TRANSCRIPT, final: finalTranscript, interim: interimTranscript }); };这里的核心是 event.results 数组。每一项代表一次识别结果isFinal为 true 表示这段文字已经定稿不会变化为 false 表示当前是实时草稿会被下一次回调覆盖。代码从 event.resultIndex 开始遍历很重要它记录了上次处理的位置避免把同一段文字拼接两次。由于 continuous 为 true这个回调会被频繁触发所以项目里用 reducer 合并状态而不是直接操作 DOM。3.3 React 状态管理如何把临时转录与最终转录拼接有了识别事件接下来是状态管理。核心 hook useSpeechRecognition 内部使用 useReducer 维护三个状态全量最终文字、当前临时文字、识别状态。这样设计的好处是所有文字变化都集中在 reducer 中处理逻辑可以被单独测试。项目里的 reducer 主干如下function reducer(state, action) { switch (action.type) { case UPDATE_TRANSCRIPT: return { ...state, finalText: state.finalText action.final, interimText: action.interim, }; case CLEAR_TRANSCRIPT: return { finalText: , interimText: }; case SET_STATUS: return { ...state, status: action.status }; default: return state; } }这里有个隐藏的坑UPDATE_TRANSCRIPT 直接拼接 finalText如果回调里把已经加入的 final 内容又重复返回就会产生重复文本。所以识别端需要记录上一次 resultIndex只处理新增部分。我在做二次开发时保留了 lastFinalIndex 变量每次 onresult 都基于它做增量拼接这样就不会出现前一句被说两遍的问题。interimText 则是每次用新值覆盖因为中间结果本身就是不稳定的。3.4 界面组件控制条与转录面板组件层相对简单。ControlBar 负责开始/停止按钮和状态提示TranscriptPanel 展示最终文字InterimPanel 用较浅颜色显示实时结果。项目把两个面板分开渲染是有道理的临时的半成品文字充满修正痕迹如果混进最终文本用户会不断看到字词跳变造成「识别不准」的错觉。停止逻辑也很关键。点击停止按钮不能直接只调 recognition.stop()而是要先 abort 丢弃未完成的中间结果再调用 stop 正常结束。项目里的处理方式是先 stop 再 abort并把这个过程封装成清除函数组件卸载时也调用它避免页面销毁后识别回调还在触发更新导致警告。这个细节很多新手会忽略我建议你在使用 hook 时一定把清理函数放进 useEffect 的返回值里。4. 跑通项目的五项工程化准备与避坑记录4.1 依赖安装与开发服务器启动项目依赖极少用 npm 安装即可。在项目根目录执行npm install npm startnpm start 会启动开发服务器默认端口 3000。实际上我不建议直接跑 npm start 完事而是先执行一次CItrue npm test确认基础测试通过再启动开发服务器。这样能把编译错误和逻辑错误提前暴露。对于这个项目运行前唯一的硬准备是确认浏览器是 Chromium 内核否则页面会提示不支持。4.2 麦克风权限请求与系统设置第一次打开页面浏览器会弹出麦克风授权请求。如果用户点了阻止之后授权弹窗不会再次出现需要在浏览器设置里手动重置权限。这是最常见的拦路虎。项目里如果没有权限检测逻辑我建议你补一段navigator.permissions.query({ name: microphone }).then((status) { if (status.state denied) { console.warn(麦克风权限被拒绝请在浏览器设置中重新授权); } });权限状态有 granted、prompt、denied 三种。granted 可以直接开始prompt 表示需要触发 getUserMedia 提醒用户denied 则需要引导用户去设置页修改。把这个检查放在初始化阶段能避免用户反复试错。4.3 避坑一转录中途「偶发」失效现象识别刚开始完全正常用了十几分钟后突然不再返回文字页面也没有明显报错控制台有一条network类型的错误。原因SpeechRecognition 虽然在浏览器里运行但语音数据实际上会发送到识别服务长时间使用会触发网络连接重置或者浏览器因为资源回收中断了持续识别。解决监听 onerror 事件捕获network错误后自动重启识别recognition.onerror (event) { if (event.error network) { recognition.abort(); setTimeout(() { recognition.start(); }, 300); } };这里不能无限重启否则会形成死循环。我一般会加一个计数器连续超过 3 次就停止并提示用户重新点击开始按钮。因为网络长期不稳定时自动重启只会增加数据量消耗。4.4 避坑二浏览器授权了但系统麦克风被静音现象浏览器弹窗已授权页面按钮也变成了「开始」状态但识别始终没有反应浏览器地址栏右侧显示麦克风被禁止的图标。原因操作系统的隐私设置可能拦截了麦克风访问或者默认输入设备是虚拟声卡没有真正拾音。浏览器授权和系统授权是两层必须都通过才行。解决先检查系统的麦克风隐私开关再在浏览器设置里选择正确的输入设备。代码层面可以增加一个 getUserMedia 探针主动请求音频流navigator.mediaDevices.getUserMedia({ audio: true }) .then((stream) { // 拿到流之后可以检查轨道的状态 const track stream.getAudioTracks()[0]; console.log(音频设备:, track.label); }) .catch((err) { console.error(麦克风不可用:, err); });这一步能提前暴露设备问题而不是等用户说完一段话才发现没有录进去。4.5 避坑三临时转录区闪烁与字数回退现象收音过程中页面上实时显示的文字不断刷新但有时文字突然消失半秒甚至比之前短了一截。原因这是中间结果的正常行为。浏览器会修正已经返回的临时文字可能把上一帧「以为听到」的内容换成新的猜测所以在展示层看到长度回退并不奇怪。很多开发者误以为是状态丢失把临时结果也拼进最终结果反而造成文本重复。解决严格区分 isFinal只把定稿内容追加到最终转录临时结果单独存放并且用浅色字体展示暗示用户这些字还没被确认。如果你不希望界面跳跃可以把 interimResults 改为 false这样只会返回稳定的定稿但实时性就没了。4.6 避坑四构建部署后出现空白页现象执行 npm run build 后把静态文件部署到服务器子目录打开页面是空白控制台报 JS 资源 404。原因项目构建时引用的 JS 地址是绝对路径/static/js/...部署在子目录时路径对不上。这是 Create React App 的默认行为本地开发看不出来上线就翻车。解决在 package.json 中设置homepage: .让构建产物改用相对路径。如果项目里用了路由还需要把路由模式改成 HashRouter否则刷新子路径会 404。修改后重新 build把 build 目录里的 index.html 拖到浏览器里也能直接打开检查。5. 二次开发实战把只读应用改造成可上线的语音转录工具到这里你应该已经能跑通项目并理解核心链路了。最后一章我讲两个改造点和验证方法这是我把这个项目接进实际工作流时积累的经验。第一个改造点是增加手动纠错。语音转录一定会有错别字尤其是专业名词。做法是把 TranscriptPanel 中展示 finalText 的元素从p换成textarea并在 blur 或 CtrlEnter 时把修改后的文本写回状态。需要注意不要使用 dangerouslySetInnerHTML 渲染文本否则用户输入的内容包含尖括号会破坏页面。我用的方案是受控组件每次修改触发一个 UPDATE_FINAL_TEXT action更新 state 中的 finalText。第二个改造点是导出带时间戳的 Markdown。浏览器在识别结果中不直接暴露时间戳但我可以通过识别开始时记录的 Date.now()再根据每次 onresult 的偏移量估算。我在 reducer 中为每个 final 段落增加 startTime 和 endTime 字段导出时生成下面这样的格式## 00:01:08 用户发言 这里是该时间段的转写内容导出功能放在 ControlBar 右侧的按钮里点击后把文本拷入剪贴板顺带下载一个 .md 文件。这样生成的速记可以直接粘贴进笔记软件省去手动排版。验证方法也很直接用一段固定音频对着麦克风播放三次连续转录三次对比结果是否稳定。我重点检查两件事修改完文本后重新开始识别旧内容是否被正确清空连续识别时上一段文字是否重复追加。做完这两个检查再把识别时长拖到十五分钟以上看自动重启逻辑是否正常。这套流程走完基本能拦住大多数回归问题。从那以后我每次做语音相关前端项目都会强制走一遍「权限检查 → 单段录音 → 长时录音 → 导出格式校验」的完整流程这几个动作能省掉不少线上投诉。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?