多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

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

@rrweb/record 实战指南:rrweb 2.x 独立录制包的安装、事件采集与隐私配置 rrweb/record 实战指南rrweb 2.x 独立录制包的安装、事件采集与隐私配置【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrwebrrweb/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/recordimport { 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 typemodule import { record } from https://cdn.rrweb.com/record/current/dist/record.js; /scriptcurrent指向最新稳定版本适合快速体验生产环境建议固定精确版本号以保证 URL 不可变例如https://cdn.rrweb.com/record/2.0.0/dist/record.js。方式三传统script直引UMD 回退仅用于不支持 ES Module 的旧环境兼容script srchttps://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 章节blockClassrr-block字符串或 RegExp命中元素不录制回放时以同尺寸占位块显示blockSelectornull字符串选择器命中元素不录制ignoreClassrr-ignore字符串或 RegExp命中元素不录制其 input 事件ignoreSelectornull字符串选择器命中元素不录制其 input 事件ignoreCSSAttributesnull需要忽略的 CSS 属性数组maskTextClassrr-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/truerecordCrossOriginIframesfalse是否录制跨域 iframe需在每个子 iframe 中注入 rrwebrecordAfterload文档未就绪时在指定事件后开始录制DOMContentLoaded/loadinlineImagesfalse2.0.0 起弃用仍受支持未来将被captureAssets取代collectFontsfalse是否采集页面字体userTriggeredOnInputfalse是否为 input 事件附加userTriggered标记区分是否由用户直接触发plugins[]加载扩展录制功能的插件见 插件 APIerrorHandler-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.tsMaskInputOptions支持按输入类型逐项掩码color、date、datetime-local、email、month、number、range、search、tel、text、time、url、textarea、select、passwordSlimDOMOptions支持裁剪script、comment、headFavicon、headWhitespace、headMetaDescKeywords、headMetaSocial、headMetaRobots、headMetaHttpEquiv、headMetaAuthorship、headMetaVerification等以及用于屏蔽 title 标签动画类高频变更的选项。源码 packages/rrweb/src/record/index.ts#L139-L163 揭示了maskInputOptions与maskAllInputs的合并逻辑当maskAllInputs true时所有上述输入类型含password一律掩码否则使用用户显式传入的maskInputOptions未传时默认{ password: true }。slimDOMOptions则统一经过slimDOMDefaults处理后再参与快照。隐私保护四种脱敏手段录制即涉及用户数据rrweb/record提供了多级隐私方案详见 guide.md.rr-block命中元素完全不录制回放时渲染为同尺寸占位块默认blockClass.rr-ignore命中元素不录制其 input 事件默认ignoreClass.rr-mask命中元素及其所有子元素的文本被掩码默认maskTextClassinput[typepassword]默认即被掩码对应maskInputOptions的默认值。!-- 示例对敏感区域打标记 -- div classrr-block卡片号码区域不录制/div div classrr-mask用户真实姓名会被掩码/div input typepassword / !-- 默认掩码 --若默认类名与业务样式冲突可通过blockSelector、ignoreSelector、maskTextSelector传入精确的 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 触发而非会话开头那次。按事件数保留出错前的最近 200400 条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, }); };按时间保留出错前的最近 510 分钟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实际会得到最后 200400 条事件checkout 触发前可能已累积近一个周期的增量checkoutEveryNms: 5 * 60 * 1000对应最近 510 分钟的事件。大多数情况下你不需要配置 checkout它只适合错误发生后仅回传最近一段会话这类场景。工程化细节产物形态与体积约束rrweb/record的构建产物由 packages/record/vite.config.ts 与根目录 vite.config.default.ts 共同决定输出esdist/record.js与cjsdist/record.cjs两种库格式并额外生成 UMD 与压缩版本UMD 产物除dist/外还会同步到umd/目录package.json中unpkg/jsdelivr字段指向umd/record.js这是为兼容 jsDelivr MIME 类型问题而做的同步副本打包时会将rrweb、rrweb-snapshot、rrdom三个依赖解析到仓库内的本地源码入口见 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 工具等完整内容存储优化packFn、sampling的实战用法插件 API为record扩展录制能力的插件体系源码核心实现record函数的完整实现观察器初始化、事件处理器、checkout 调度录制相关测试跨域 iframe、mutation、错误处理等录制行为的测试用例可作为行为规范参考。【免费下载链接】rrwebrecord and replay the web项目地址: https://gitcode.com/gh_mirrors/rr/rrweb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表