前端开发中,你是否经常遇到这样的困境:用户反馈“按钮点击无响应”或“页面空白”,而你却只能依靠有限的日志来推测现场,难以真实复现用户的操作路径?
这种信息差不仅严重拖慢了问题排查效率,更可能导致关键业务故障持续影响用户体验。前端录制回放技术的出现,正是为了解决这一痛点。它通过精准复现“用户操作 - 页面状态 - 最终结果”的全链路,为问题诊断和行为分析提供了“上帝视角”,彻底打破了“事后难追溯”的困境。
从实际业务价值来看,这项技术在多个场景中尤为突出:
- 电商支付环节:用户反馈“支付后订单未生成但余额已扣”,传统日志无法确认是否因网络延迟导致重复点击。而录制回放可以快速定位“因多次提交引发的前端状态混乱”,将排查时间从小时级缩短至分钟级。
- 企业SaaS运维:当CRM系统出现“客户资料保存后字段丢失”的问题,且仅由特定操作顺序触发时,传统排查方法容易陷入僵局。录制回放能直接发现“因标签页快速切换导致组件未提交数据就销毁”的边缘场景。
- 金融风控合规:录制转账、实名认证等关键操作的完整页面状态,能够满足监管部门对“操作可追溯”的严格要求。
- 远程技术支持:客服人员无需再依赖用户模糊的描述,通过查看录制文件即可直观了解故障发生过程,大幅提升技术支持效率。
这些场景的共同点,是对“前端行为可观测性”的极致需求。传统的错误日志、性能上报等监控手段只能提供碎片化的信息,而录制回放技术构建了完整的行为链路,让前端问题从“难以复现”变为“精准可追溯”。像 rrweb 这类工具的出现,为中高级前端工程师应对复杂场景提供了全新的技术方案。
rrweb 核心概念与特性
什么是 rrweb?
rrweb(Record and Replay the Web)是一款开源的前端录制回放工具。它的核心定位是提供一套轻量级、高保真、可扩展的 Web 页面录制与回放解决方案。
与传统的视频流录制不同,rrweb 通过捕获 DOM 变化、用户交互事件以及页面状态,生成结构化的 JSON 数据,再基于这些数据重建操作流程。这种“数据化录制”模式,使其在文件体积、浏览器兼容性以及功能扩展性上都远超传统方案,特别适合集成到 Vue 3、React 等现代前端框架的项目中,用于问题排查、用户行为分析与异常监控。
从架构上看,rrweb 主要由三大核心模块构成:
rrweb:核心录制库,负责捕获页面变化与事件。
rrweb-snapshot:DOM 快照处理库,负责将 DOM 序列化为可传输、可重建的数据。
rrweb-player:回放组件库,提供录制数据的播放与交互功能。
三者既可组合使用形成完整链路,也可独立运作(例如单独使用 rrweb-snapshot 处理 DOM 序列化),这种模块化设计是其能灵活适配现代前端工程化生态的关键。
为何选择 rrweb?与传统方案对比
在 rrweb 出现之前,前端录制主要依赖“截图拼接”或“视频录制”,但两者都存在明显局限,难以满足对精准性、轻量性和可分析性的高阶需求。
1. 视频录制方案的缺陷
基于浏览器插件或客户端工具的视频录制,核心问题是“笨重”且“不可分析”:
- 体积庞大:1分钟操作可能生成数十MB文件,大规模监控会激增带宽与存储成本。
- 无法深度分析:仅提供视觉证据,无法关联到具体的 DOM 结构、JavaScript 变量状态。例如,你看到“按钮点击无反应”,却无法知道是事件未绑定、接口报错还是状态管理异常。
- 性能影响:对终端设备配置要求较高,在低配设备上可能导致卡顿,干扰用户正常操作。
2. 截图拼接方案的不足
通过定时截图再拼接成“伪回放”视频,虽然解决了体积问题,却牺牲了“连续性”和“高保真”:
- 采样间隔难以平衡:间隔短则数据量大,间隔长则易丢失关键操作瞬间。
- 交互动态缺失:无法复现鼠标悬停、输入框实时输入等连续性交互行为。
- 技术信息断层:没有记录 DOM 树和 CSS 样式数据,若问题源于 DOM 结构异常或样式覆盖,则完全无法定位根源。
3. rrweb 的核心优势
相比之下,rrweb 的“数据化录制”模式完美规避了上述问题,其优势集中体现在四个方面:
- 极致的轻量:生成 JSON 格式数据,体积通常仅为视频文件的 1/100 到 1/10(1分钟操作约 100KB ~ 1MB),几乎没有传输与存储压力。
- 精准的高保真:能够精准复现 DOM 变化、用户交互与页面状态,甚至可以还原“网络延迟时的 loading 状态”、“组件异步渲染顺序”等细节。
- 强大的可分析性:录制数据包含了完整的 DOM 序列化信息与事件日志,回放时可随时查看任意时刻的 DOM 结构、CSS 样式,还能关联
Vuex/Redux 等状态管理库的数据,实现从“现象”到“代码根源”的直接跳转。
- 出色的兼容性:支持 Chrome、Firefox、Safari 10+ 等主流浏览器,对前端框架无侵入——无论是
Vue 3、React 还是传统的 jQuery 项目,都可以轻松集成,无需修改业务代码。
rrweb 的核心特性
除了上述对比优势,rrweb 还提供了一系列定制化特性,能够精准解决企业级项目中的复杂痛点:
1. 隐私保护机制
支持通过配置过滤敏感信息。你可以设置“黑名单”(指定不录制的元素)或“白名单”(仅录制特定区域),以满足金融、医疗等行业严格的合规要求。例如,在登录页面可以通过配置过滤密码输入框,避免敏感数据被录制。
2. 增量录制与断点续录
- 增量录制:先录制首屏完整的 DOM 快照,后续仅捕获增量变化(如元素新增、属性修改),大幅缩减数据量。
- 断点续录:在网络中断恢复后可以继续录制,无需重启,非常适合表单填写等长时间操作场景。
3. 现代前端框架友好性
针对 Vue 3、React 等框架做了专项适配,避免虚拟 DOM 更新可能导致的录制偏差:
- 在
Vue 3 项目中,能够识别组件的 onMounted 生命周期,捕获 DOM 的最终挂载状态,而非虚拟 DOM 未挂载时的临时结构。
- 支持通过
customEvent 配置捕获框架的自定义事件(如 Vue 的 $emit),让录制数据更贴合实际的开发逻辑。
4. 可扩展的插件体系
rrweb 提供了丰富的接口,支持开发者开发自定义插件,极大地扩展了其能力边界:
- 性能监控插件:同步捕获
FCP、LCP 等核心性能指标。
- 接口关联插件:将
Axios/fetch 等网络请求日志与录制时间轴绑定,实现“接口报错时页面状态”的精准复现。
- 错误捕获插件:自动关联
window.onerror 捕获的运行时错误与对应的录制片段,实现“错误 - 用户操作”的精准匹配。
这些特性从隐私合规、框架适配到生态扩展,全面覆盖了中高级工程师在实际开发中的需求,让 rrweb 不再只是一个简单的“录制工具”,而是成为构建前端可观测性体系的核心组件。
快速上手:实现基础录制与回放
本章将基于实际代码,从依赖安装、核心配置解析、录制功能实现到回放功能落地,带你完整走一遍 rrweb 基础集成的全流程。所有代码均可直接复用,同时我们会深入讲解关键配置背后的设计逻辑,帮助你理解“为什么这么配置”,而不仅仅是“怎么配置”。
前置准备:安装依赖与环境配置
rrweb 的录制回放能力依赖三个核心包,但在实际项目中,rrweb-snapshot 会被 rrweb 自动引入,你通常只需要显式安装 rrweb(录制核心)和 rrweb-player(回放组件)即可。同时,必须引入播放器的样式文件以确保 UI 正常渲染。
1. 安装命令
根据你的项目包管理器选择对应命令(推荐使用 pnpm):
# 方案1:使用 pnpm 安装(推荐,速度更快且依赖树更扁平)
pnpm install rrweb rrweb-player
# 方案2:使用 npm 安装
npm install rrweb rrweb-player
注意:虽然 rrweb-snapshot 可被自动引入,但在多人协作项目中显式安装可以锁定版本,避免出现“我这儿能运行,他那儿报错”的兼容性问题。
2. 模块引入
在你的 Vue 3 组件(或 React 组件、原生 JS 文件)中引入所需模块。特别要注意,rrweb-player 的样式文件必须引入,否则回放控制器会没有样式。
// 1. 引入录制核心函数
import { record } from 'rrweb';
// 2. 引入回放组件(默认导出类)
import rrwebPlayer from 'rrweb-player';
// 3. 引入回放组件样式(关键!否则控制器无样式)
import 'rrweb-player/dist/style.css';
// 4. (Vue 3 项目)引入响应式API,非Vue项目可用普通变量
import { ref } from 'vue';
核心状态管理:定义关键变量
在实现具体功能前,我们需要先定义存储 “录制状态” 、“录制数据” 和 “配置项” 的变量。这些变量是连接“录制”与“回放”两个环节的核心纽带。以下以 Vue 3 的响应式变量为例:
// 1. 存储录制的事件数组(rrweb 录制的核心数据,回放时需传入此数组)
const events = ref([]);
// 2. 存储录制配置项(用对象统一管理,便于后续扩展和修改)
const recordConfig = ref({
maskAllInputs: true, // 隐私保护:是否对所有输入框内容脱敏
maskInputPassword: true, // 隐私保护:单独控制密码输入框脱敏(优先级高于 maskAllInputs)
blockClass: 'rrweb-block', // 隐私保护:带此类名的元素会被完全屏蔽(不录制其内容)
recordCanvas: false, // 功能配置:是否录制 Canvas 内容(开启会增加数据量)
samplingInterval: 100, // 性能优化:滚动事件采样间隔(单位ms,0表示不采样)
});
// 3. 录制状态标识(控制按钮显示/隐藏、防止重复录制)
const isRecording = ref(false);
// 4. 是否有录制数据(控制回放按钮是否可用)
const hasRecording = ref(false);
// 5. 存储停止录制的函数(rrweb.record() 会返回此函数,需保存以便后续调用)
let stopFn = null;
// 6. 存储回放实例(控制回放的暂停、销毁等操作)
let player = null;
// 7. 回放容器DOM引用(需绑定到页面中的回放容器元素)
const replayContainer = ref(null);
设计思路:将配置项用 recordConfig 对象统一管理,而不是散落在代码各处,既能提高可读性,也便于后续开发可视化的“配置修改面板”。
实现录制功能:从配置到启停
录制是 rrweb 的核心能力。其核心逻辑是调用 rrweb.record(options) 创建录制实例,通过 options.emit 回调收集 DOM 变化事件,同时利用各种配置项在“录制精度”、“数据体积”和“隐私安全”之间取得平衡。
1. 核心录制函数 (startRecord)
// 开始录制函数:整合配置、创建录制实例、收集事件
const startRecord = () => {
// 1. 初始化状态:清空历史录制数据,避免新旧数据混淆
events.value = [];
console.log('### 开始录制,当前配置:', recordConfig.value);
// 2. 配置录制选项(rrweb 的核心配置,每一项都影响录制效果)
const options = {
/**
* 关键回调:每次DOM变化/用户交互时触发
* @param {Object} event - rrweb 生成的事件对象(包含事件类型、目标元素、时间戳等)
* 作用:收集所有事件到 events 数组,为后续回放提供数据
*/
emit(event) {
events.value.push(event);
},
// -------------------------- 隐私保护配置 --------------------------
/**
* maskAllInputs:是否对所有输入框(input/textarea)内容脱敏
* 效果:输入框内容会被替换为 "●●●",避免录制手机号、密码等敏感信息
* 场景:适用于整站录制,无需区分输入框类型的场景
*/
maskAllInputs: recordConfig.value.maskAllInputs,
/**
* maskInputOptions:精细化控制输入框脱敏(优先级高于 maskAllInputs)
* 支持的键:password/email/number/tel/text 等 input 类型
* 场景:需要“密码脱敏但普通文本不脱敏”的场景(如仅保护密码框)
*/
maskInputOptions: {
password: recordConfig.value.maskInputPassword,
},
/**
* blockClass:指定“完全屏蔽”的类名
* 效果:带此类名的元素会被录制为“黑色块”,内容和交互都不记录
* 场景:屏蔽广告、隐私弹窗、用户头像等绝对不能录制的元素
* 使用方式:在DOM元素上添加 class="rrweb-block" 即可
*/
blockClass: recordConfig.value.blockClass,
// -------------------------- 功能配置 --------------------------
/**
* recordCanvas:是否录制 Canvas 内容
* 原理:通过截取 Canvas 帧并转为 base64 存储(会大幅增加数据量)
* 注意:若页面有高频刷新的 Canvas(如游戏、图表),不建议开启
*/
recordCanvas: recordConfig.value.recordCanvas,
/**
* inlineStylesheet:是否将样式表内联到录制数据中
* 效果:回放时无需依赖原页面的CSS文件,保证样式一致性
* 必要性:若回放环境与录制环境样式不同(如测试服vs生产服),必须开启
*/
inlineStylesheet: true,
// -------------------------- 性能优化:采样配置 --------------------------
/**
* sampling:控制高频事件的录制频率,减少数据体积
* 适用事件:mousemove(鼠标移动)、scroll(滚动)、mouseInteraction(鼠标交互)
* 设计逻辑:高频事件(如滚动)无需每帧记录,间隔100ms记录一次即可满足回放需求
*/
sampling: recordConfig.value.samplingInterval > 0 ? {
mousemove: true, // 鼠标移动:开启采样(默认每100ms记录一次)
mouseInteraction: true, // 鼠标交互(点击、悬停):开启采样
scroll: recordConfig.value.samplingInterval, // 滚动事件:按配置间隔采样
input: 'all' // 输入事件:记录所有(不采样,避免丢失输入内容)
} : {
input: 'all' // 若采样间隔为0,仅记录输入事件(适用于对数据体积要求极高的场景)
}
};
// 3. 调用 rrweb.record() 开始录制,返回“停止函数”并保存
// 注意:每次调用 record() 都会生成新实例,需先停止旧实例再开启新录制
stopFn = record(options);
// 4. 更新状态:控制UI显示(如录制按钮变“停止按钮”)
isRecording.value = true;
hasRecording.value = false;
};
2. 停止录制函数 (stopRecord)
停止录制的逻辑相对简单,核心是调用 startRecord 中保存的 stopFn,并更新状态标识。
const stopRecord = () => {
// 防止重复调用:若 stopFn 不存在(未开始录制),直接返回
if (!stopFn) return;
// 调用停止函数:销毁录制实例,停止收集事件
stopFn();
// 清空 stopFn:避免后续误调用
stopFn = null;
// 更新状态:录制状态置为false,判断是否有有效录制数据
isRecording.value = false;
hasRecording.value = events.value.length > 0;
// 日志输出:便于调试(如录制数据量异常时,可查看事件数量)
console.log(`### 停止录制,共收集 ${events.value.length} 个事件`);
};
调试技巧:如果发现停止录制后 events 数组仍为空,可以检查以下几点:1. 页面是否有 DOM 变化或用户交互发生;2. maskAllInputs 等隐私配置是否误屏蔽了所有元素;3. 组件是否在录制完成前被销毁,导致 emit 回调未执行。
实现回放功能:基于录制数据重建场景
回放的核心是通过 rrweb-player 库创建回放实例,将 events 数组传入,让播放器按照事件的时间戳依次执行,从而重建录制时的页面场景。
1. 核心回放函数 (replay)
const replay = () => {
// 防护逻辑:若无录制数据,直接返回(避免报错)
if (events.value.length === 0) {
console.warn('### 无录制数据,无法回放');
return;
}
// 清理旧实例:若已有回放实例,先暂停并销毁(避免多实例冲突)
if (player) {
player.pause(); // 暂停回放
player = null; // 清空实例引用(触发垃圾回收)
}
// 创建回放实例:核心是传入录制的 events 数组和容器
player = new rrwebPlayer({
/**
* target:回放容器DOM元素
* 要求:容器必须已挂载到DOM树(Vue 3中需用 ref 绑定,确保不是 null)
* 建议:容器设置固定宽高,避免回放时变形
*/
target: replayContainer.value,
/**
* props:回放配置项(控制播放行为和UI)
*/
props: {
events: events.value, // 核心数据:录制的事件数组(必须传入)
width: replayContainer.value.clientWidth, // 回放宽度(与容器一致,避免拉伸)
height: replayContainer.value.clientHeight, // 回放高度(与容器一致)
autoPlay: true, // 自动播放:无需手动点击“播放”按钮
showController: true, // 显示控制器:包含播放/暂停、速度调节、进度条
speedOption: [1, 2, 4, 8], // 播放速度选项:支持1x(正常)、2x(倍速)等
showTime: true, // 显示录制时间戳(便于定位特定时间点的问题)
},
});
};
2. 页面 UI 绑定(Vue 3 模板示例)
回放功能需要绑定“录制/停止/回放按钮”和“回放容器”,以下是可直接整合到组件中的 Vue 3 模板代码:
<template>
<div class="rrweb-demo">
<!-- 1. 操作按钮区域:控制录制启停和回放 -->
<div class="control-buttons">
<!-- 录制/停止按钮:根据 isRecording 切换文本和事件 -->
<button @click="isRecording ? stopRecord() : startRecord()">
{{ isRecording ? '停止录制' : '开始录制' }}
</button>
<!-- 回放按钮:仅当有录制数据时可用 -->
<button @click="replay()" :disabled="!hasRecording">
开始回放
</button>
</div>
<!-- 2. 回放容器:必须设置宽高,绑定 ref 供 JS 调用 -->
<div class="replay-container" ref="replayContainer"></div>
</div>
</template>
<style scoped>
.rrweb-demo {
padding: 20px;
}
.control-buttons {
margin-bottom: 20px;
gap: 10px;
display: flex;
}
.control-buttons button {
padding: 8px 16px;
cursor: pointer;
}
/* 回放容器:固定高度,添加边框便于识别 */
.replay-container {
width: 100%;
height: 600px;
border: 1px solid #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
</style>
验证与调试:确保功能正常运行
完成代码编写后,需要通过简单的操作验证功能是否正常,同时也需要掌握常见问题的调试方法。
1. 功能验证
你可以点击“开始录制”按钮,在页面上进行一些操作(如点击、输入、滚动),然后点击“停止录制”,再点击“开始回放”。如果一切正常,你应该能在下方的回放容器中看到刚才所有操作的完整复现。

上图展示了一个 rrweb 录制回放的演示界面,左侧为包含多种交互元素(如拖拽盒子、点击计数器、含敏感信息的输入框)的操作区,右侧为回放显示区域。
2. 常见问题调试
-
问题 1:回放容器空白
- 排查方向:
- 检查
replayContainer 是否绑定正确(可在控制台打印 replayContainer.value 确认不是 null)。
- 检查
events 数组是否有数据(若为空,需检查 startRecord 中的 emit 回调是否执行)。
-
问题 2:输入框内容未脱敏
- 排查方向:
- 确认
maskAllInputs 或 maskInputPassword 配置是否为 true。
- 检查输入框是否有
rrweb-block 类(此类会完全屏蔽元素,而非脱敏)。
-
问题 3:回放数据体积过大
- 优化方向:
- 开启
sampling 配置(如设置 samplingInterval: 200)。
- 关闭
recordCanvas(如果页面没有必须录制的 Canvas 内容)。
- 通过
blockClass 屏蔽无需录制的区域(如广告栏、侧边栏)。
高级功能详解:隐私、性能与数据管理
在掌握了基础录制回放之后,rrweb 提供的高级特性是其能否胜任企业级复杂场景的关键。从用户隐私保护到录制性能优化,再到录制数据的全生命周期管理,这些功能直接决定了 rrweb 在实际项目中的可用性与安全性。
隐私保护机制(输入掩码与元素屏蔽)
前端录制本质上是对用户操作与页面内容的“全量捕获”,但在金融、医疗、电商等场景中,页面常常包含身份证号、银行卡号、密码等敏感信息,直接录制会引发严重的隐私合规风险。rrweb 提供的“输入掩码”与“元素屏蔽”机制,正是为了解决这一核心痛点,实现“精准录制”与“隐私保护”的平衡。
1. 输入掩码:定向隐藏敏感输入内容
输入掩码功能通过配置,让 rrweb 在录制时自动将指定类型的输入框内容替换为占位符(如“*”),回放时仅显示占位符,不泄露真实数据。它主要适配两类场景:“全量输入掩码”和“定向输入掩码”。
-
代码实现与配置解析
// 录制配置中的隐私保护选项
const recordConfig = ref({
maskAllInputs: false, // 全局开关:是否掩码所有输入字段
maskInputPassword: true, // 定向开关:仅掩码密码类型输入框
});
// 集成到 rrweb 录制选项中
const options = {
// 其他配置...
maskAllInputs: recordConfig.value.maskAllInputs, // 全局掩码开关
maskInputOptions: {
password: recordConfig.value.maskInputPassword, // 密码字段单独控制
},
};
-
效果与应用场景
- 当
maskInputPassword: true 时,所有 type="password" 的输入框内容在录制数据中会被替换为“*”,回放时看不到真实密码。适用于登录页、支付密码输入等场景。
- 当
maskAllInputs: true 时,无论输入框类型(文本、手机号、银行卡号),其内容都会被掩码。适用于页面包含多种敏感输入的场景,如金融 App 的实名认证页面、医疗系统的患者信息填写页。
-
最佳实践建议
- 优先使用“定向掩码”:全局掩码会隐藏所有输入内容,可能导致排查问题时无法获取关键操作(如用户输入的搜索关键词)。建议仅对明确的敏感字段(如
password、tel)进行单独配置。
- 结合业务预设默认值:例如,在支付页面默认开启“密码掩码”,在普通的个人信息填写页则默认关闭全局掩码。
- 录制前给予用户提示:若录制涉及用户操作,应在开始前明确告知“敏感输入信息会被脱敏处理”,避免引起用户的隐私顾虑。
2. 元素屏蔽:整体隐藏敏感区域
除了输入内容,页面中可能存在整体的敏感区域(如用户个人头像、完整的收货地址卡片、医疗报告详情)。这时就需要“元素屏蔽”功能——通过给元素添加指定的类名,让 rrweb 在录制时直接将该元素替换为一个灰色块,回放时完全看不到原始内容。
-
代码实现与配置解析
const recordConfig = ref({
blockClass: 'rr-block', // 屏蔽元素的类名(可自定义)
});
// 集成到 rrweb 录制选项
const options = {
// 其他配置...
blockClass: recordConfig.value.blockClass, // 指定屏蔽类名
};
在模板中,只需给敏感元素添加该类名即可:
<!-- 普通内容:正常录制 -->
<div style="padding: 16px; background: #f0f9ff;">
<strong>普通订单信息</strong>
<p>订单号:202405201234567</p>
<p>商品:前端开发实战教程</p>
</div>
<!-- 敏感内容:添加 rr-block 类名,录制时被完全屏蔽 -->
<div class="rr-block" style="padding: 16px; background: #fef2f2; margin-top: 16px;">
<strong>收货地址(敏感信息)</strong>
<p>收件人:张三</p>
<p>电话:138****5678</p>
<p>地址:北京市朝阳区XX小区1号楼1单元101</p>
</div>
-
效果与应用场景
- 录制时:
rrweb 会检测所有带 rr-block 类名的元素,将其内容替换为“灰色占位块”,且不会捕获该元素内部的任何 DOM 变化或输入事件。
- 回放时:用户看到的是灰色块,完全无法获取屏蔽区域的原始内容。适用于“整段内容均敏感”的场景,如用户个人信息卡片、企业内部数据报表、医疗诊断结果详情等。
-
最佳实践建议
- 使用独特的屏蔽类名:建议使用带前缀的类名(如
rr-),避免与业务现有的 CSS 类名冲突,导致误屏蔽。
- 屏蔽区域不宜过大:如果将页面的大部分区域都屏蔽,会导致录制数据失去排查价值。建议仅对“必要且明确的敏感区域”使用此功能。
- 动态条件屏蔽:对于
Vue/React 组件,可以通过条件绑定类名实现动态屏蔽,例如 :class="{ 'rr-block': userRole === 'guest' }",实现仅对访客用户屏蔽管理员数据。
录制配置优化(采样率与 Canvas 录制)
在包含大量动画、Canvas 绘图或频繁滚动操作的复杂页面中,默认的录制配置可能会导致“数据量过大”或“性能消耗高”的问题。rrweb 提供的“采样率控制”与“Canvas 录制开关”,能帮助开发者在“录制精度”与“性能/数据量”之间找到最佳平衡点。
1. 采样率控制:减少冗余录制数据
页面中的高频事件(如鼠标移动、滚动)会产生大量冗余数据,但在大多数场景下,我们无需记录每一次事件。通过“采样率配置”,可以控制这类事件的录制频率,从而大幅减少数据量。
-
代码实现与配置解析
const recordConfig = ref({
samplingInterval: 200, // 采样间隔(单位:ms),0表示不采样
});
// 集成到 rrweb 录制选项
const options = {
// 其他配置...
sampling: recordConfig.value.samplingInterval > 0 ? {
mousemove: true, // 开启鼠标移动事件采样
mouseInteraction: true, // 开启鼠标交互(点击、hover)事件采样
scroll: recordConfig.value.samplingInterval, // 滚动事件采样间隔
input: 'all' // 输入事件不采样(记录所有输入,确保数据完整)
} : {
input: 'all' // 采样间隔为0时,仅确保输入事件完整记录
},
};
-
效果与应用场景
- 当
samplingInterval: 200 时,滚动事件每 200ms 仅记录 1 次,相比不采样(可能每秒数十次),数据量可减少 70% 以上。
- 鼠标移动事件采样后,仅记录关键的移动轨迹点,既保证了回放时的视觉流畅度,又显著减少了数据量。
- 适用场景:包含长列表滚动(如电商商品列表)、复杂动画、高频鼠标操作(如在线绘图工具)的页面。
-
最佳实践建议
- 推荐采样间隔:200-500ms 是“数据量”与“回放精度”的一个较好的平衡点。低于 200ms 数据量下降不明显,高于 500ms 可能导致回放时操作看起来有“跳跃感”。
- 输入事件切勿采样:
input: 'all' 这个配置必须保留,因为用户输入的内容(如表单填写)是后续问题排查的关键信息,采样会导致内容缺失。
- 动态调整采样率:可以根据页面类型自动切换配置。例如,对于信息流列表页面设置较高的采样间隔(如 300ms),对于核心表单填写页面则设置为 0(不采样),确保每一步输入都被完整记录。
2. Canvas 录制:按需捕获绘图内容
Canvas 绘图(如游戏、数据可视化图表、在线白板)的录制需要特殊处理。默认情况下,rrweb 不会捕获 Canvas 内部的像素变化,回放时只能看到 Canvas 的初始状态。通过 recordCanvas 配置可以开启此功能,但需注意其对性能和数据量的影响。
-
代码实现与配置解析
const recordConfig = ref({
recordCanvas: true, // 是否录制 Canvas 内容(默认关闭)
inlineImages: false // 是否内联 Canvas 图片(显著影响数据量)
});
// 集成到 rrweb 录制选项
const options = {
// 其他配置...
recordCanvas: recordConfig.value.recordCanvas, // Canvas 录制开关
inlineImages: recordConfig.value.inlineImages, // 图片内联开关
};
-
效果与应用场景
- 当
recordCanvas: true 时,rrweb 会定期捕获 Canvas 的像素数据(以图片形式)并记录到事件中,回放时可完整复现绘图过程。
- 当
inlineImages: true 时,Canvas 图片会以 Base64 格式内联到 JSON 数据中,这会导致数据量急剧增加。
- 适用场景:包含动态
Canvas 内容的页面,如在线绘图工具、游戏 Demo、实时数据可视化大屏。对于仅展示静态图表的 Canvas,则无需开启。
-
最佳实践建议
- 按需开启:仅在需要复现
Canvas 动态变化时开启此功能。
- 谨慎使用图片内联:如果
Canvas 变化非常频繁,建议关闭 inlineImages,并自定义逻辑将图片上传到服务器,录制数据中只保存图片 URL,以控制 JSON 体积。
- 控制捕获频率:可以通过
rrweb 的插件机制或自定义扩展,调整捕获 Canvas 帧的间隔,避免高频捕获导致前端卡顿。
数据管理(下载、上传与大小计算)
录制数据的“存储、传输与复用”是 rrweb 落地的关键环节。开发者需要能将数据下载到本地用于调试,或上传到服务器用于团队共享和异常监控分析。
1. 数据大小计算:实时掌握数据体积
录制数据的大小直接影响下载速度、存储成本与传输效率。实时计算数据大小,可以帮助开发者判断是否需要优化录制配置。
2. 下载录制数据:本地存储与调试
将录制数据下载为 JSON 文件,可用于本地调试、离线分析或归档备份。
-
代码实现
const downloadRecording = () => {
if (events.value.length === 0) {
alert('没有录制数据可下载');
return;
}
// 1. 序列化事件数组(缩进格式,便于阅读)
const data = JSON.stringify(events.value, null, 2);
// 2. 创建 Blob 对象
const blob = new Blob([data], { type: 'application/json' });
// 3. 生成临时 URL
const url = URL.createObjectURL(blob);
// 4. 创建隐藏的 a 标签触发下载
const a = document.createElement('a');
a.href = url;
a.download = `rrweb-recording-${Date.now()}.json`; // 文件名包含时间戳
document.body.appendChild(a);
a.click();
// 5. 清理临时资源
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
3. 上传录制数据:复用与共享
上传功能允许将本地保存的 JSON 录制文件重新导入,实现跨设备回放、团队共享调试或异常数据上报,是数据“复用”的核心。
-
代码实现核心逻辑
const uploadRecording = (event) => {
const file = event.target.files[0];
if (!file) return;
// 校验文件格式
if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
alert('请上传 JSON 格式的录制文件');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
// 基础校验:是否为非空数组
if (!Array.isArray(data) || data.length === 0) {
throw new Error('录制数据为空或格式错误');
}
// 导入数据
events.value = data;
hasRecording.value = true;
console.log('上传成功!', data.length, '个事件');
} catch (error) {
console.error('上传失败:', error);
alert(`上传失败:${error.message}`);
}
};
reader.readAsText(file);
event.target.value = ''; // 重置input,允许重复上传同一文件
};
通过以上完整的配置、实现与优化指南,你可以将 rrweb 深度集成到你的前端项目中,无论是用于提升开发调试效率,还是构建强大的用户行为分析与线上问题监控体系,它都将成为一个不可或缺的利器。在实践中遇到的具体问题,欢迎在 云栈社区 的前端技术板块与更多开发者交流探讨。