@rrweb/record 实战指南:rrweb 2.x 独立录制包的安装、事件采集与隐私配置
2026/9/20 15:44:35 网站建设 项目流程

@rrweb/record 实战指南:rrweb 2.x 独立录制包的安装、事件采集与隐私配置

【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrweb

@rrweb/record是 rrweb 在 2.x 时代拆出的独立录制包,专门承载网页录制(record)相关代码,面向需要在前端应用或网页中采集用户操作事件(DOM 变化、滚动、交互、Canvas 等)的生产场景。本文围绕 packages/record/README.md 展开,完整覆盖其三种安装方式、record函数的核心用法、全量参数表、隐私脱敏与 Checkout 快照机制,并结合仓库源码与测试用例给出可复现的实操结论。读完本文,你将能独立完成@rrweb/record的接入、事件上报、按需全量快照与敏感内容屏蔽。

包定位与设计动机

在 rrweb 2.x 的包拆分方案中,录制与回放被拆成两个独立包:@rrweb/record负责录制,@rrweb/replay负责回放,另有@rrweb/all提供一键合并导入。官方 guide.md 明确指出:在大多数生产场景中,录制端与回放端部署在不同页面/应用中,因此录制页使用@rrweb/record、回放页使用@rrweb/replay(或rrweb-player)是推荐组合,而rrweb主包已被标记为 deprecated,新项目应直接使用拆分后的包。

@rrweb/record的源码目前非常精简——packages/record/src/index.ts 只有三行,本质是从rrweb主包 re-exportrecord函数:

import { record } from 'rrweb'; export { record };

文档 Notes 部分也明确说明:当前该包实质上只是主rrweb包中record函数的包装器,未来所有 record 相关代码都会迁移至此。因此,本包的全部能力(选项解析、快照、观察器等)实际实现位于 packages/rrweb/src/record/index.ts,阅读该文件即可理解record的底层原理。

此外,rrweb 官方的云端录制浏览器客户端browser-client也是基于本包构建的,且接受与record完全相同的 options——仓库中对应的实现在 packages/browser-client。如果你需要基于 WebSocket 对接 rrweb Cloud API,可在该包基础上二次开发。

安装与引入的三种方式

方式一:Bundler / npm(推荐)

npm install @rrweb/record
import { record } from '@rrweb/record';

从 packages/record/package.json 可以看到,该包以 ESM 为主路径:exports字段同时提供了import./dist/record.js)与require./dist/record.cjs)两个入口并各自配套类型声明(.d.ts/.d.cts),因此 CommonJS 的require('@rrweb/record')依然可用,但 2.x 官方推荐 ESM 导入。

方式二:浏览器直引 ESM(无构建)

在不使用打包器的场景下,直接通过 CDN 加载浏览器 ESM 产物:

<script type="module"> import { record } from 'https://cdn.rrweb.com/record/current/dist/record.js'; </script>
  • current指向最新稳定版本,适合快速体验;
  • 生产环境建议固定精确版本号以保证 URL 不可变,例如https://cdn.rrweb.com/record/2.0.0/dist/record.js

方式三:传统<script>直引(UMD 回退)

仅用于不支持 ES Module 的旧环境兼容:

<script src="https://cdn.rrweb.com/record/current/dist/record.umd.cjs"></script>

加载后全局变量名为rrwebRecord。该全局名也直接体现在打包配置中——packages/record/vite.config.ts 在调用公共构建配置时传入'rrwebRecord'作为库名,与 vite.config.default.ts 中基于 esbuildumdWrapper生成 UMD 与压缩产物的逻辑相衔接。

快速上手:启动一次录制

record的用法非常直接——传入一个包含emit回调的 options 对象即可:

import { record } from '@rrweb/record'; record({ emit(event) { // 将 event 发送到你的服务端 }, });

录制期间,只要页面发生任何事件(DOM 变更、滚动、输入、鼠标交互等),录制器就会通过emit回调吐出事件对象。源码 packages/rrweb/src/record/index.ts#L122-L125 中有对应的运行时校验:在主录制帧内(inEmittingFrame且未跨域转发)必须提供emit,否则直接抛出'emit function is required'

record方法返回一个停止函数,调用后不再产生新事件:

let stopFn = record({ emit(event) { if (events.length > 100) { stopFn(); // 采集满 100 个事件后停止 } }, });

注意:record的返回类型是listenerHandler | undefined,在某些场景(例如跨域 iframe 子帧中,父帧已负责采集时)会返回空操作函数,因此调用停止函数前建议判空。

一个真实的上报示例

将采集到的事件批量 POST 到后端(以 rrweb Cloud API 为例):

const publicApiKey = 'your-public-api-key-here'; const recordingId = crypto.randomUUID(); let events = []; record({ emit(event) { events.push(event); }, }); // 将事件发送到后端并重置数组 function save() { const body = JSON.stringify({ events }); events = []; fetch(`https://api.rrweb.com/recordings/${recordingId}/events`, { method: 'POST', headers: { Authorization: `Bearer ${publicApiKey}`, 'Content-Type': 'application/json', }, body, }); } // 每 10 秒保存一次 setInterval(save, 10 * 1000);

record 核心选项总览

record函数接受一个 options 对象,完整参数表(与 guide.md 一致)如下:

key默认值说明
emit必填接收录制事件的回调函数
checkoutEveryNth-每 N 个事件后输出一次全量快照,见下文 Checkout 章节
checkoutEveryNms-每 N 毫秒后输出一次全量快照,见下文 Checkout 章节
blockClass'rr-block'字符串或 RegExp,命中元素不录制,回放时以同尺寸占位块显示
blockSelectornull字符串选择器,命中元素不录制
ignoreClass'rr-ignore'字符串或 RegExp,命中元素不录制其 input 事件
ignoreSelectornull字符串选择器,命中元素不录制其 input 事件
ignoreCSSAttributesnull需要忽略的 CSS 属性数组
maskTextClass'rr-mask'字符串或 RegExp,命中元素及其子元素的文本被脱敏
maskTextSelectornull字符串选择器,命中元素及其子元素的文本被脱敏
maskAllInputsfalse将所有 input 内容统一掩码为*
maskInputOptions{ password: true }按输入类型掩码,见下文
maskInputFn-自定义 input 内容录制逻辑
maskTextFn-自定义文本内容录制逻辑
slimDOMOptions{}移除 DOM 中的冗余部分,见下文
dataURLOptions{}Canvas 图片格式与质量,会传给OffscreenCanvas.convertToBlob(),可有效减小录制数据体积
inlineStylesheettrue2.0.0 起弃用,仍受支持,未来将被captureAssets取代
hooks{}事件级钩子
packFn-事件压缩函数,见 存储优化指南
sampling-事件采样配置,见 存储优化指南
recordCanvasfalse是否录制 canvas 元素(false/true
recordCrossOriginIframesfalse是否录制跨域 iframe(需在每个子 iframe 中注入 rrweb)
recordAfter'load'文档未就绪时,在指定事件后开始录制(DOMContentLoaded/load
inlineImagesfalse2.0.0 起弃用,仍受支持,未来将被captureAssets取代
collectFontsfalse是否采集页面字体
userTriggeredOnInputfalse是否为 input 事件附加userTriggered标记(区分是否由用户直接触发)
plugins[]加载扩展录制功能的插件,见 插件 API
errorHandler-rrweb 内部抛出错误时的回调,接收 error 参数

从源码 packages/rrweb/src/record/index.ts#L68-L102 可以确认这些选项的解构与默认值逻辑,其中几个细节值得注意:

  • recordAfter只接受'DOMContentLoaded',其他任何值(包括未传)统一按'load'处理;
  • ignoreCSSAttributes在源码中被转换为Set使用;
  • 若同时传了已弃用的mousemoveWait与新的sampling.mousemove,源码会优先保留sampling.mousemovemousemoveWait仅在sampling.mousemove未定义时被迁移过去)。

maskInputOptions 与 slimDOMOptions 的完整取值

这两个选项的类型定义位于 packages/rrweb-snapshot/src/types.ts:

  • MaskInputOptions支持按输入类型逐项掩码:colordatedatetime-localemailmonthnumberrangesearchteltexttimeurltextareaselectpassword
  • SlimDOMOptions支持裁剪:scriptcommentheadFaviconheadWhitespaceheadMetaDescKeywordsheadMetaSocialheadMetaRobotsheadMetaHttpEquivheadMetaAuthorshipheadMetaVerification等,以及用于屏蔽 title 标签"动画"类高频变更的选项。

源码 packages/rrweb/src/record/index.ts#L139-L163 揭示了maskInputOptionsmaskAllInputs的合并逻辑:当maskAllInputs === true时,所有上述输入类型(含password)一律掩码;否则使用用户显式传入的maskInputOptions,未传时默认{ password: true }slimDOMOptions则统一经过slimDOMDefaults处理后再参与快照。

隐私保护:四种脱敏手段

录制即涉及用户数据,@rrweb/record提供了多级隐私方案(详见 guide.md):

  • .rr-block:命中元素完全不录制,回放时渲染为同尺寸占位块(默认blockClass);
  • .rr-ignore:命中元素不录制其 input 事件(默认ignoreClass);
  • .rr-mask:命中元素及其所有子元素的文本被掩码(默认maskTextClass);
  • input[type="password"]:默认即被掩码(对应maskInputOptions的默认值)。
<!-- 示例:对敏感区域打标记 --> <div class="rr-block">卡片号码区域,不录制</div> <div class="rr-mask">用户真实姓名会被掩码</div> <input type="password" /> <!-- 默认掩码 -->

若默认类名与业务样式冲突,可通过blockSelectorignoreSelectormaskTextSelector传入精确的 CSS 选择器;需要更细粒度的控制时,可用maskInputFn/maskTextFn自定义掩码逻辑,用ignoreCSSAttributes跳过特定 CSS 属性的录制。

Checkout:按事件数或时间输出全量快照

rrweb 的事件流遵循"初始全量快照(FullSnapshot)+ 后续增量快照(IncrementalSnapshot)"的链式结构,要回放完整会话必须持有链上全部事件。checkoutEveryNth/checkoutEveryNms允许你周期性强制输出新的全量快照,从而把事件流切成多个自洽的片段,便于只保留错误发生前的最后若干段。

先看源码层面的触发逻辑(packages/rrweb/src/record/index.ts#L213-L234):

  • 每当发出FullSnapshot事件时,记录lastFullSnapshotEvent并重置增量计数;
  • 每当发出IncrementalSnapshot事件时计数加一,当checkoutEveryNth(按条数)或checkoutEveryNms(按与上次全量快照的时间差)任一条件满足时,调用takeFullSnapshot(true)强制输出新的全量快照;
  • emit回调的第二个参数isCheckout即为该全量快照是否由 checkout 触发(而非会话开头那次)。

按事件数:保留出错前的最近 200~400 条

const publicApiKey = 'your-public-api-key-here'; const recordingId = crypto.randomUUID(); // 用二维数组存放多个事件片段 const eventsMatrix = [[]]; record({ emit(event, isCheckout) { if (isCheckout) { eventsMatrix.push([]); } const lastEvents = eventsMatrix[eventsMatrix.length - 1]; lastEvents.push(event); }, checkoutEveryNth: 200, // 每 200 个事件输出一次全量快照 }); // 出错时把最近两个片段发给后端 window.onerror = function () { const len = eventsMatrix.length; const events = eventsMatrix[len - 2].concat(eventsMatrix[len - 1]); const body = JSON.stringify({ events }); fetch(`https://api.rrweb.com/recordings/${recordingId}/events`, { method: 'POST', headers: { Authorization: `Bearer ${publicApiKey}`, 'Content-Type': 'application/json', }, body, }); };

按时间:保留出错前的最近 5~10 分钟

record({ emit(event, isCheckout) { if (isCheckout) { eventsMatrix.push([]); } const lastEvents = eventsMatrix[eventsMatrix.length - 1]; lastEvents.push(event); }, checkoutEveryNms: 5 * 60 * 1000, // 每 5 分钟输出一次全量快照 });

边界说明

指南 guide.md 特别提醒:由于增量快照链的机制限制,无法精确截取"最后 N 条"事件。checkoutEveryNth: 200实际会得到最后 200~400 条事件(checkout 触发前可能已累积近一个周期的增量);checkoutEveryNms: 5 * 60 * 1000对应最近 5~10 分钟的事件。大多数情况下你不需要配置 checkout,它只适合"错误发生后仅回传最近一段会话"这类场景。

工程化细节:产物形态与体积约束

@rrweb/record的构建产物由 packages/record/vite.config.ts 与根目录 vite.config.default.ts 共同决定:

  • 输出esdist/record.js)与cjsdist/record.cjs)两种库格式,并额外生成 UMD 与压缩版本;
  • UMD 产物除dist/外还会同步到umd/目录(package.jsonunpkg/jsdelivr字段指向umd/record.js),这是为兼容 jsDelivr MIME 类型问题而做的同步副本;
  • 打包时会将rrwebrrweb-snapshotrrdom三个依赖解析到仓库内的本地源码入口(见 packages/record/vite.config.ts 中的sourceEntryByPackageName),保证按最新源码出包;
  • 全局库名rrwebRecord服务于 UMD 直引场景。

值得注意的是,packages/record/test/record.test.ts 通过测试对产物体积做了硬约束:注释记录了 tree-shaking 修复前 ESM bundle 为 397373 字节、修复后为 161287 字节,并断言产物中不得包含回放专用的 postcss 代码,且dist/record.js体积必须比修复前基线至少小 200 KiB——这从测试侧印证了"录制包只打包录制代码、不携带回放代码"的设计目标,是录制端体积优化的可验证依据。

延伸阅读

  • guide.md:官方入门指南,包含 Replayer、rrweb-player 与 REPL 工具等完整内容;
  • 存储优化:packFnsampling的实战用法;
  • 插件 API:为record扩展录制能力的插件体系;
  • 源码核心实现:record函数的完整实现(观察器初始化、事件处理器、checkout 调度);
  • 录制相关测试:跨域 iframe、mutation、错误处理等录制行为的测试用例,可作为行为规范参考。

【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrweb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询