必须显式调用navigator.mediaDevices.getUserMedia({ audio: true })请求权限,需在HTTPS或localhost安全上下文下、用户手势触发后执行,用try/catch捕获NotAllowedError、NotFoundError等异常,成功后保存MediaStream用于后续MediaRecorder初始化。

如何用 navigator.mediaDevices.getUserMedia() 获取麦克风权限
浏览器不会默认给你麦克风权限,必须显式调用 getUserMedia() 并请求音频流。不处理拒绝或错误,按钮点下去就静默失败,用户根本不知道发生了什么。
- 必须在 HTTPS 环境下运行(本地
localhost也允许,但http://127.0.0.1某些旧版 Chrome 会拒绝) - 调用时要明确传入
{ audio: true },不能只写true或漏掉对象包装 - 务必用
try/catch包裹,捕获NotAllowedError(用户点“拒绝”)、NotFoundError(没插麦克风)、NotReadableError(设备被占用)等常见错误 - 成功后得到的
MediaStream对象要保存下来,后续录音要用它创建MediaRecorder
为什么 MediaRecorder 初始化后立刻 start() 会报错
MediaRecorder 构造函数本身不检查流是否有效,但 start() 时如果流已结束、轨道被禁用、或浏览器不支持该 mimeType,就会抛出 InvalidStateError 或 TypeError。
- 初始化前确认
stream.getAudioTracks().length > 0,避免传入空流 - 推荐指定 mimeType:如
new MediaRecorder(stream, { mimeType: 'audio/webm' }),Chrome 支持webm,Safari 目前只认mp4(需额外适配) - 不要在
stream上调用getTracks()[0].enabled = false后还试图录音——轨道被禁用会导致start()失败 - 某些安卓 WebView 对
MediaRecorder支持不稳定,可加降级逻辑:先尝试webm,失败则 fallback 到audio/wav(需手动编码)
点击按钮时如何避免重复启动/停止录音
用户连点两次“开始”,或快速切换“开始→停止→开始”,容易触发状态冲突,比如对已停止的 MediaRecorder 再调 stop(),抛出 InvalidStateError。
使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
- 用一个状态变量(如
isRecording)严格控制按钮行为,点击前先判断当前状态 - 监听
recorder.onstart和recorder.onstop更新状态,而不是仅靠按钮点击事件 -
stop()后立即设recorder = null,防止下次误用旧实例 - 按钮文字和
disabled属性要同步更新,例如开始录音时设btn.disabled = true,避免 UI 与实际状态脱节
录音结束后怎么拿到可用的音频文件
MediaRecorder 的 dataavailable 事件每次吐出一个 Blob 片段,不是完整文件。直接用最后一个 Blob 当成品,大概率只有几帧音频。
立即学习“前端免费学习笔记(深入)”;
- 用数组(如
chunks = [])收集所有dataavailable触发的Blob - 在
stop事件里合并:const blob = new Blob(chunks, { type: 'audio/webm' }) - 生成下载链接:
const url = URL.createObjectURL(blob),再赋给<a href="${url}" download="recording.webm"> - 注意内存:调用
URL.revokeObjectURL(url)在下载完成后及时释放,否则 Blob 会常驻内存
真正难的不是录,是让不同浏览器都拿到能播放的文件——Safari 的 mp4 需要 ffmpeg.wasm 转封装,而 Android Chrome 的采样率可能不匹配播放器要求。这些细节不处理,用户点完“下载”打不开文件,比录不了更让人抓狂。


















