指南
安装
| 目标 | 推荐的软件包 |
|---|---|
| 大多数项目(录制 + 回放) | @rrweb/record + @rrweb/replay |
| 快速接入,一次导入录制、回放和压缩打包(packer)功能 | @rrweb/all |
在大多数生产部署中,录制器和回放器会部署在不同的页面/应用中。在被录制的页面上使用 @rrweb/record,在回放页面上使用 @rrweb/replay(或 rrweb-player)。当你确实希望用单个包来换取便利时(例如演示、工具或简化接入的场景),可以使用 @rrweb/all。
rrweb包已废弃。它仍然可用,但新项目应使用@rrweb/record和@rrweb/replay(或使用@rrweb/all进行单次导入),以便我们精简并最终移除rrweb。
1) 打包器 / npm(推荐)
npm install @rrweb/record @rrweb/replayimport { record } from '@rrweb/record';
import { Replayer } from '@rrweb/replay';
import '@rrweb/replay/dist/style.css';如果你希望只用一个导入,可以使用便捷包 @rrweb/all:
npm install @rrweb/allimport { record, Replayer } from '@rrweb/all';
import '@rrweb/all/dist/style.css';通过各包的 exports/main,require(...) / CommonJS 仍然可用以保持兼容,但 ESM 导入是 2.x 的主要方式。
2) 不使用打包器的浏览器环境(免构建)
使用来自 CDN 的浏览器 ESM 资源:
<link
rel="stylesheet"
href="https://cdn.rrweb.com/replay/current/dist/style.css"
/>
<script type="module">
import { record } from 'https://cdn.rrweb.com/record/current/dist/record.js';
import { Replayer } from 'https://cdn.rrweb.com/replay/current/dist/replay.js';
record({
emit(event) {
console.log(event);
},
});
</script>使用 current 可以获取最新的稳定版本;也可以锁定具体版本号,例如 https://cdn.rrweb.com/record/2.0.0/dist/record.js 和 https://cdn.rrweb.com/replay/2.0.0/dist/replay.js,以获得不可变的 生产环境 URL。
rrweb-player 同样提供浏览器 ESM 资源:
<link
rel="stylesheet"
href="https://cdn.rrweb.com/rrweb-player/current/style.css"
/>
<script type="module">
import rrwebPlayer from 'https://cdn.rrweb.com/rrweb-player/current/rrweb-player.js';
</script>3) 传统的直接 <script> 引入方式(UMD 回退方案)
仅在需要兼容不支持模块的环境时使用此方式。
<script src="https://cdn.rrweb.com/record/current/dist/record.umd.cjs"></script>
<script src="https://cdn.rrweb.com/replay/current/dist/replay.umd.cjs"></script>UMD 构建会暴露 rrwebRecord 和 rrwebReplay 全局变量。对于现代浏览器, 请优先使用 ESM CDN 资源。
其他软件包
完整的 rrweb 软件包列表及说明,请参见软件包参考。
兼容性说明
rrweb 不支持 IE11 及以下版本,因为它使用了 MutationObserver API,该 API 受这些浏览器支持。
快速开始
录制
在现代接入方式中,使用 @rrweb/record 提供的 record:
import { record } from '@rrweb/record';record({
emit(event) {
// store the event in any way you like
},
});在录制过程中,每当有事件产生,录制器就会触发 emit;你需要做的只是以任意你喜欢的方式存储这些发出的事件。
record 方法会返回一个函数,调用它可以停止事件的触发:
let stopFn = record({
emit(event) {
if (events.length > 100) {
// stop after 100 events
stopFn();
}
},
});一个更接近真实场景的用法可能如下所示:
const publicApiKey = 'your-public-api-key-here';
const recordingId = crypto.randomUUID();
let events = [];
record({
emit(event) {
// push event into the events array
events.push(event);
},
});
// this function will send events to the backend and reset the events array
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,
});
}
// save events every 10 seconds
setInterval(save, 10 * 1000);录制选项
record 函数接受以下选项。
| 键 | 默认值 | 描述 |
|---|---|---|
| emit | 必填 | 用于接收发出的事件的回调函数 |
| checkoutEveryNth | - | 每经过 N 个事件拍摄一次全量快照 参见检查点一节 |
| checkoutEveryNms | - | 每隔 N 毫秒拍摄一次全量快照 参见检查点一节 |
| blockClass | 'rr-block' | 使用字符串或 RegExp 配置需要屏蔽的元素,参见隐私一节 |
| blockSelector | null | 使用字符串配置需要屏蔽的选择器,参见隐私一节 |
| ignoreClass | 'rr-ignore' | 使用字符串或 RegExp 配置需要忽略的元素,参见隐私一节 |
| ignoreSelector | null | 使用字符串配置需要忽略的选择器,参见隐私一节 |
| ignoreCSSAttributes | null | 需要忽略的 CSS 属性数组 |
| maskTextClass | 'rr-mask' | 使用字符串或 RegExp 配置需要脱敏的元素,参见隐私一节 |
| maskTextSelector | null | 使用字符串配置需要脱敏的选择器,参见隐私一节 |
| maskAllInputs | false | 将所有输入内容脱敏为 * |
| maskInputOptions | { password: true } | 对特定类型的输入进行脱敏 * 参见列表 |
| maskInputFn | - | 自定义输入内容脱敏的录制逻辑 |
| maskTextFn | - | 自定义文本内容脱敏的录制逻辑 |
| slimDOMOptions | {} | 移除 DOM 中不必要的部分 参见列表 |
| dataURLOptions | {} | canvas 图像的格式和质量,该参数会传递给 OffscreenCanvas.convertToBlob(),使用此参数可以有效减小录制数据的体积 |
| inlineStylesheet | true | 自 2.0.0 起废弃。仍然受支持,但计划由未来的 captureAssets 资源录制 API 取代。 |
| hooks | {} | 事件的钩子 参见列表 |
| packFn | - | 参见存储优化配方 |
| sampling | - | 参见存储优化配方 |
| recordCanvas | false | 是否录制 canvas 元素。可选值:false、true |
| recordCrossOriginIframes | false | 是否录制跨域 iframe。要使该功能生效,必须在每个子 iframe 中注入 rrweb。可选值:false、true |
| recordAfter | 'load' | 如果文档尚未就绪,录制器会在指定事件触发后才开始录制。可选值:DOMContentLoaded、load |
| inlineImages | false | 自 2.0.0 起废弃。仍然受支持,但计划由未来的 captureAssets 资源录制 API 取代。 |
| collectFonts | false | 是否收集网站中的字体 |
| userTriggeredOnInput | false | 是否在 input 事件上添加 userTriggered 标记,用于指示该事件是否由用户直接触发。什么是 userTriggered? |
| plugins | [] | 加载插件以提供扩展的录制功能。什么是插件? |
| errorHandler | - | 当 rrweb 内部抛出错误时调用的回调,该回调接收错误对象作为参数。 |
隐私
如果网页上有些内容你不希望被录制,可以采用以下方式:
- 带有
.rr-block类名的元素不会被录制,回放时会以相同尺寸的占位元素显示。 - 带有
.rr-ignore类名的元素不会录制其输入事件。 - 带有
.rr-mask类名的元素及其所有子元素的文本都会被脱敏。 input[type="password"]默认会被脱敏。- 可通过脱敏选项对输入元素中的内容进行脱敏。
检查点
默认情况下,回放一个会话需要所有已发出的事件;如果你不想存储全部事件,可以使用检查点(checkout)配置。
大多数情况下你不需要配置此项。但如果你想实现类似“只在错误发生后保留最后 N 个事件”的功能,可以参考以下示例:
const publicApiKey = 'your-public-api-key-here';
const recordingId = crypto.randomUUID();
// We use a two-dimensional array to store multiple events array
const eventsMatrix = [[]];
record({
emit(event, isCheckout) {
// isCheckout is a flag to tell you the events has been checkout
if (isCheckout) {
eventsMatrix.push([]);
}
const lastEvents = eventsMatrix[eventsMatrix.length - 1];
lastEvents.push(event);
},
checkoutEveryNth: 200, // checkout every 200 events
});
// send last two events array to the backend
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,
});
};由于 rrweb 采用增量快照链机制,我们无法精确地只获取最后 N 个事件。使用上面的示例代码,最终发送到你后端的会是最后 200 到 400 个事件。
类似地,你也可以配置 checkoutEveryNms 来获取最近 N 分钟的事件:
const publicApiKey = 'your-public-api-key-here';
const recordingId = crypto.randomUUID();
// We use a two-dimensional array to store multiple events array
const eventsMatrix = [[]];
record({
emit(event, isCheckout) {
// isCheckout is a flag to tell you the events has been checkout
if (isCheckout) {
eventsMatrix.push([]);
}
const lastEvents = eventsMatrix[eventsMatrix.length - 1];
lastEvents.push(event);
},
checkoutEveryNms: 5 * 60 * 1000, // checkout every 5 minutes
});
// send last two events array to the backend
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 分钟的事件。
回放
使用打包器时,在应用入口处引入样式表:
import '@rrweb/replay/dist/style.css';在浏览器免构建场景中,引入样式表并从 CDN 导入回放器:
<link
rel="stylesheet"
href="https://cdn.rrweb.com/replay/current/dist/style.css"
/>
<script type="module">
import { Replayer } from 'https://cdn.rrweb.com/replay/current/dist/replay.js';
const events = YOUR_EVENTS;
const replayer = new Replayer(events);
replayer.play();
</script>通过 API 控制回放器
const replayer = new Replayer(events);
// play
replayer.play();
// play from the third seconds
replayer.play(3000);
// pause
replayer.pause();
// pause at the fifth seconds
replayer.pause(5000);
// destroy the replayer (hint: this operation is irreversible)
replayer.destroy();回放选项
回放器接受选项作为其构造函数的第二个参数,支持以下选项:
| 键 | 默认值 | 描述 |
|---|---|---|
| speed | 1 | 回放速度倍率 |
| root | document.body | 回放器的根元素 |
| loadTimeout | 0 | 加载远程样式表的超时时间 |
| skipInactive | false | 是否跳过无交互的时间 |
| inactivePeriodThreshold | 10000 | 判定无交互时间段的阈值(毫秒) |
| showWarning | true | 是否在回放过程中打印警告信息 |
| showDebug | false | 是否在回放过程中打印调试信息 |
| blockClass | 'rr-block' | 带有该类名的元素会显示为屏蔽区域 |
| liveMode | false | 是否启用实时模式 |
| insertStyleRules | [] | 接受多条 CSS 规则字符串,会被注入到回放 iframe 中 |
| triggerFocus | true | 是否在回放过程中触发焦点 |
| UNSAFE_replayCanvas | false | 是否回放 canvas 元素。启用后会向回放 iframe 添加 allow-scripts,从而脱离沙箱的脚本执行保护,这是不安全的。 |
| pauseAnimation | true | 回放器暂停时是否同时暂停 CSS 动画 |
| mouseTail | true | 是否在回放中显示鼠标拖尾。设为 false 可禁用鼠标拖尾。完整配置参见此类型定义 |
| unpackFn | - | 参见存储优化配方 |
| logConfig | - | 控制台输出回放的配置,参见控制台配方 |
| plugins | [] | 加载插件以提供扩展的回放功能。什么是插件? |
| useVirtualDom | true | 跳转到新时间点时是否使用 Virtual DOM 优化 |
| logger | console | 回放器用于打印警告或错误的 logger 对象 |
使用 rrweb-player
由于 @rrweb/replay 的 Replayer 只提供基础 UI,你可以选择 rrweb-player,它基于 rrweb 的公开 API 构建,提供功能更丰富的回放器 UI。
安装
打包器 / npm(推荐):
npm install rrweb-playerimport rrwebPlayer from 'rrweb-player';
import 'rrweb-player/dist/style.css';不使用打包器的浏览器环境(ESM):
<link
rel="stylesheet"
href="https://cdn.rrweb.com/rrweb-player/current/style.css"
/>
<script type="module">
import rrwebPlayer from 'https://cdn.rrweb.com/rrweb-player/current/rrweb-player.js';
</script>传统的直接 <script> 引入方式(UMD 回退方案):
<link
rel="stylesheet"
href="https://cdn.rrweb.com/rrweb-player/current/style.css"
/>
<script src="https://cdn.rrweb.com/rrweb-player/current/rrweb-player.umd.cjs"></script>用法
new rrwebPlayer({
target: document.body, // customizable root element
props: {
events,
},
});选项
| 键 | 默认值 | 描述 |
|---|---|---|
| events | [] | 用于回放的事件 |
| width | 1024 | 回放器的宽度 |
| height | 576 | 回放器的高度 |
| maxScale | 1 | 回放器的最大缩放比例(1 = 100%),设为 0 表示不限制 |
| autoPlay | true | 是否自动播放 |
| speedOption | [1, 2, 4, 8] | UI 中的速度选项 |
| showController | true | 是否显示控制器 UI |
| tags | {} | 使用键值对映射自定义各类自定义事件的样式 |
| ... | - | 其他所有 Replayer 选项都会被透传 |
事件
开发者可能希望扩展回放器或响应其事件,例如在开始跳过无交互时间时通知用户。 Replayer 暴露了公开 API on,开发者可以用它监听事件并自定义行为:
const replayer = new Replayer(events);
replayer.on(EVENT_NAME, (payload) => {
...
})事件列表:
| 事件 | 描述 | 值 |
|---|---|---|
| start | 开始回放 | - |
| pause | 暂停回放 | - |
| finish | 回放结束 | - |
| resize | 视口尺寸发生变化 | { width, height } |
| fullsnapshot-rebuilded | 重建了全量快照 | event |
| load-stylesheet-start | 开始加载远程样式表 | - |
| load-stylesheet-end | 远程样式表加载完成 | - |
| skip-start | 开始跳过无交互时间 | { speed } |
| skip-end | 无交互时间跳过完成 | { speed } |
| mouse-interaction | 鼠标交互已被回放 | { type, target } |
| event-cast | 事件已被回放 | event |
| custom-event | 自定义事件已被回放 | event |
| destroy | 回放器已销毁 | - |
rrweb-replayer 也通过 component.addEventListener API 重新暴露了事件监听。
另外,rrweb-replayer 还会以同样的方式发出三个事件:
| 事件 | 描述 | 值 |
|---|---|---|
| ui-update-current-time | 当前时间发生变化 | { payload } |
| ui-update-player-state | 当前播放器状态发生变化 | { payload } |
| ui-update-progress | 当前进度发生变化 | { payload } |
REPL 工具
你也可以使用无需安装的 REPL 测试工具来体验 rrweb。
运行 yarn repl 启动浏览器,并在命令行中根据提示输入你想测试的 URL:
Enter the url you want to record, e.g https://example.com:等待浏览器打开指定页面,命令行会打印以下信息:
Enter the url you want to record, e.g https://example.com: https://github.com
Going to open https://github.com...
Ready to record. You can do any interaction on the page.
Once you want to finish the recording, enter 'y' to start replay:此时你可以在网页上进行交互。待需要录制的操作完成后,在命令行输入 'y',测试工具就会回放这些操作,以验证录制是否成功。
回放时命令行会打印以下信息:
Enter 'y' to persistently store these recorded events:此时你可以再次在命令行输入 'y',测试工具会将录制的会话保存为静态 HTML 文件,并提示保存位置:
Saved at PATH_TO_YOUR_REPO/temp/replay_2018_11_23T07_53_30.html该文件使用最新的 rrweb 打包代码,因此我们可以在修改代码后运行 npm run bundle:browser,然后刷新该静态文件,查看并调试最新代码对回放的影响。