vue-tab-recorder
Vue 3 headless 页签录屏 composable:MediaRecorder 录制(页签音频+麦克风混音),Chrome 126+ 原生 MP4 直出,Chrome 84+ 经 ffmpeg.wasm 转码 MP4
165
1
| Install | |
|---|---|
npm install vue-tab-recorder |
|
| Latest Version: | 0.1.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Sep 19, 2026 |
| Links: | GitHub · npm |
Maintainer: xiaolinzzz
vue-tab-recorder
Vue 3 headless 页签录屏 composable。只提供录制逻辑,不含任何 UI——按钮、弹窗、上传接口全部由使用方自行实现。
- 画面:
getDisplayMedia捕获当前页签(Chrome 72+) - 声音:页签音频(Chrome 74+,需用户在选择器勾选"分享页签音频")+ 麦克风,经 AudioContext 混音为单轨
- 产物:Chrome 126+ 原生 MP4 直出(H.264 + AAC);Chrome 84–125 录制 webm 后经 ffmpeg.wasm 转码 MP4
- 运行环境要求:HTTPS 或 localhost
安装
npm install vue-tab-recorder
部署 ffmpeg core(仅旧浏览器兜底需要)
Chrome 84–125 的转码依赖 ffmpeg.wasm core 三件套,需要能被 HTTP 直接访问。把包内 core/ 目录拷贝到项目的静态目录:
# Vite 项目(public 为静态目录)
cp -r node_modules/vue-tab-recorder/core public/ffmpeg-0.11.0
拷贝后默认路径约定即生效(包按 <页面base>/ffmpeg-0.11.0/ffmpeg-core.js 解析)。如需放到其他位置,通过 corePath 选项或 setFfmpegCorePath() 指定:
useScreenRecorder({ corePath: '/static/my-ffmpeg/ffmpeg-core.js' })
只需支持 Chrome 126+ 时可跳过本步骤(不会走转码路径,core 不会被加载)。
使用
<script setup>
import { useScreenRecorder } from 'vue-tab-recorder'
const {
isRecording, // Ref<boolean> 是否录制中
duration, // Ref<number> 录制时长(秒)
recordedBlob, // Ref<Blob|null> 录制产物(停止后写入)
recordedUrl, // Ref<string> 产物 ObjectURL(下载/预览用)
recordedFileName, // Ref<string> 产物文件名(扩展名跟随实际格式)
transcoding, // Ref<boolean> webm 转码 MP4 中
transcodeProgress,// Ref<number> 转码进度 0~1
isSupported, // () => boolean 环境能力检测
start, // () => Promise<void> 开始录制(必须在点击手势内调用)
stop, // () => void 停止录制
reset, // () => void 释放资源并清空产物
} = useScreenRecorder({
// 非阻断提示(麦克风不可用/转码失败降级保留 webm 等),默认 console.warn
onWarn: (msg) => { /* 接入你的 toast */ },
// 阻断错误(启动失败等),默认 console.error
onError: (msg) => { /* 接入你的 toast */ },
// 产物文件名前缀,默认 "录屏"
fileNamePrefix: '录屏',
})
// 保存本地
const saveLocal = () => {
const a = document.createElement('a')
a.href = recordedUrl.value
a.download = recordedFileName.value
a.click()
reset()
}
// 上传:包不涉接口,把 recordedBlob 交给你自己的接口即可
const upload = async () => {
const form = new FormData()
form.append('file', new File([recordedBlob.value], recordedFileName.value))
await fetch('/your/upload/api', { method: 'POST', body: form })
reset()
}
</script>
<template>
<button v-if="!isRecording" @click="start">录屏</button>
<button v-else @click="stop">{{ duration }}s 停止</button>
<div v-if="transcoding">转码中 {{ Math.round(transcodeProgress * 100) }}%</div>
</template>
状态流转说明
start()→ 浏览器弹出分享选择器(需用户手势,授权等待期间重复调用会被忽略);- 录制中
isRecording=true、duration每秒递增;用户点浏览器"停止共享"条也会正常走停止流程; stop()后:- Chrome 126+:
recordedBlob立即就绪(MP4); - 旧浏览器:
transcoding=true进入转码,transcodeProgress推进,完成后recordedBlob就绪(MP4);转码失败时自动降级保留 webm 产物并触发onWarn;
- Chrome 126+:
- 保存/上传/丢弃后调用
reset()清理 ObjectURL 与采集资源。
组件卸载时自动 reset()(在组件 setup 内调用时生效)。
低级 API
import { loadFFmpeg, transcode, transcodeToMp4, setFfmpegCorePath } from 'vue-tab-recorder'
await transcodeToMp4(webmBlob, (progress) => {}) // 单文件 webm → mp4
await transcode(webmBlob, ['-i', 'input.webm', '...', 'output.mp4']) // 自定义 ffmpeg 参数
兼容性矩阵
| 浏览器 | 路径 | 说明 |
|---|---|---|
| Chrome 126+ | 原生 MP4 直出 | 无转码开销 |
| Chrome 91–125 | webm → ffmpeg.wasm 转码 | 需部署 core |
| Chrome 84–90 | webm → ffmpeg.wasm 0.11 转码 | 需部署 core;转码强制单线程(规避 core 多线程代理缺陷),1080p 长视频转码耗时可能接近录制时长 |
发布前检查(npm publish)
- 确认
package.json的name在 npmjs 上可用,必要时改名; npm publish --access public(包内files已限定为src/core/README.md,core 约 9MB)。
Related Packages
xgplayer-vue
Vue component for xgplayer, a HTML5 video player with a parser that saves traffi...
4,850
241
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.0 | ^3.2.0 | MIT |