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>

状态流转说明

  1. start() → 浏览器弹出分享选择器(需用户手势,授权等待期间重复调用会被忽略);
  2. 录制中 isRecording=true、duration 每秒递增;用户点浏览器"停止共享"条也会正常走停止流程;
  3. stop() 后:
    • Chrome 126+:recordedBlob 立即就绪(MP4);
    • 旧浏览器:transcoding=true 进入转码,transcodeProgress 推进,完成后 recordedBlob 就绪(MP4);转码失败时自动降级保留 webm 产物并触发 onWarn;
  4. 保存/上传/丢弃后调用 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)

  1. 确认 package.json 的 name 在 npmjs 上可用,必要时改名;
  2. npm publish --access public(包内 files 已限定为 src/core/README.md,core 约 9MB)。

Related Packages

hrm-player

A videojs player component for Vue.js

1,299 21
xgplayer-vue

Vue component for xgplayer, a HTML5 video player with a parser that saves traffi...

4,850 241
ezy-board-viewer

Ezy 画板 / 云笔记预览组件:渲染、翻页、缩放,并支持导出 SVG / MP4 / PDF

1,259 0
webready

Make your media web-ready: responsive AVIF/WebP images and MP4 video with ready-...

357 2
@aicut/vue

Vue 3 wrapper for the AiCut video editor + lighting picker — thin declarative sh...

3,134 5

Version History

Version Released Vue License
0.1.0 ^3.2.0 MIT