Skip to content

指南

你可能还想阅读配方,了解一些真实场景下的使用案例;也可以阅读内部设计文档,了解 rrweb 的更多技术细节。

安装

目标推荐的软件包
大多数项目(录制 + 回放)@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(推荐)

shell
npm install @rrweb/record @rrweb/replay
js
import { record } from '@rrweb/record';
import { Replayer } from '@rrweb/replay';
import '@rrweb/replay/dist/style.css';

如果你希望只用一个导入,可以使用便捷包 @rrweb/all

shell
npm install @rrweb/all
js
import { record, Replayer } from '@rrweb/all';
import '@rrweb/all/dist/style.css';

通过各包的 exports/mainrequire(...) / CommonJS 仍然可用以保持兼容,但 ESM 导入是 2.x 的主要方式。

2) 不使用打包器的浏览器环境(免构建)

使用来自 CDN 的浏览器 ESM 资源:

html
<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.jshttps://cdn.rrweb.com/replay/2.0.0/dist/replay.js,以获得不可变的 生产环境 URL。

rrweb-player 同样提供浏览器 ESM 资源:

html
<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 回退方案)

仅在需要兼容不支持模块的环境时使用此方式。

html
<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 构建会暴露 rrwebRecordrrwebReplay 全局变量。对于现代浏览器, 请优先使用 ESM CDN 资源。

其他软件包

完整的 rrweb 软件包列表及说明,请参见软件包参考

兼容性说明

rrweb 支持 IE11 及以下版本,因为它使用了 MutationObserver API,该 API 受这些浏览器支持。

快速开始

录制

在现代接入方式中,使用 @rrweb/record 提供的 record

js
import { record } from '@rrweb/record';
js
record({
  emit(event) {
    // store the event in any way you like
  },
});

在录制过程中,每当有事件产生,录制器就会触发 emit;你需要做的只是以任意你喜欢的方式存储这些发出的事件。

record 方法会返回一个函数,调用它可以停止事件的触发:

js
let stopFn = record({
  emit(event) {
    if (events.length > 100) {
      // stop after 100 events
      stopFn();
    }
  },
});

一个更接近真实场景的用法可能如下所示:

js
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 配置需要屏蔽的元素,参见隐私一节
blockSelectornull使用字符串配置需要屏蔽的选择器,参见隐私一节
ignoreClass'rr-ignore'使用字符串或 RegExp 配置需要忽略的元素,参见隐私一节
ignoreSelectornull使用字符串配置需要忽略的选择器,参见隐私一节
ignoreCSSAttributesnull需要忽略的 CSS 属性数组
maskTextClass'rr-mask'使用字符串或 RegExp 配置需要脱敏的元素,参见隐私一节
maskTextSelectornull使用字符串配置需要脱敏的选择器,参见隐私一节
maskAllInputsfalse将所有输入内容脱敏为 *
maskInputOptions{ password: true }对特定类型的输入进行脱敏 *
参见列表
maskInputFn-自定义输入内容脱敏的录制逻辑
maskTextFn-自定义文本内容脱敏的录制逻辑
slimDOMOptions{}移除 DOM 中不必要的部分
参见列表
dataURLOptions{}canvas 图像的格式和质量,该参数会传递给 OffscreenCanvas.convertToBlob(),使用此参数可以有效减小录制数据的体积
inlineStylesheettrue自 2.0.0 起废弃。仍然受支持,但计划由未来的 captureAssets 资源录制 API 取代。
hooks{}事件的钩子
参见列表
packFn-参见存储优化配方
sampling-参见存储优化配方
recordCanvasfalse是否录制 canvas 元素。可选值:
false
true
recordCrossOriginIframesfalse是否录制跨域 iframe。要使该功能生效,必须在每个子 iframe 中注入 rrweb。可选值:
false
true
recordAfter'load'如果文档尚未就绪,录制器会在指定事件触发后才开始录制。可选值:DOMContentLoadedload
inlineImagesfalse自 2.0.0 起废弃。仍然受支持,但计划由未来的 captureAssets 资源录制 API 取代。
collectFontsfalse是否收集网站中的字体
userTriggeredOnInputfalse是否在 input 事件上添加 userTriggered 标记,用于指示该事件是否由用户直接触发。什么是 userTriggered
plugins[]加载插件以提供扩展的录制功能。什么是插件?
errorHandler-当 rrweb 内部抛出错误时调用的回调,该回调接收错误对象作为参数。

隐私

如果网页上有些内容你不希望被录制,可以采用以下方式:

  • 带有 .rr-block 类名的元素不会被录制,回放时会以相同尺寸的占位元素显示。
  • 带有 .rr-ignore 类名的元素不会录制其输入事件。
  • 带有 .rr-mask 类名的元素及其所有子元素的文本都会被脱敏。
  • input[type="password"] 默认会被脱敏。
  • 可通过脱敏选项对输入元素中的内容进行脱敏。

检查点

默认情况下,回放一个会话需要所有已发出的事件;如果你不想存储全部事件,可以使用检查点(checkout)配置。

大多数情况下你不需要配置此项。但如果你想实现类似“只在错误发生后保留最后 N 个事件”的功能,可以参考以下示例:

js
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 分钟的事件:

js
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 分钟的事件。

回放

使用打包器时,在应用入口处引入样式表:

js
import '@rrweb/replay/dist/style.css';

在浏览器免构建场景中,引入样式表并从 CDN 导入回放器:

html
<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 控制回放器

js
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();

回放选项

回放器接受选项作为其构造函数的第二个参数,支持以下选项:

默认值描述
speed1回放速度倍率
rootdocument.body回放器的根元素
loadTimeout0加载远程样式表的超时时间
skipInactivefalse是否跳过无交互的时间
inactivePeriodThreshold10000判定无交互时间段的阈值(毫秒)
showWarningtrue是否在回放过程中打印警告信息
showDebugfalse是否在回放过程中打印调试信息
blockClass'rr-block'带有该类名的元素会显示为屏蔽区域
liveModefalse是否启用实时模式
insertStyleRules[]接受多条 CSS 规则字符串,会被注入到回放 iframe 中
triggerFocustrue是否在回放过程中触发焦点
UNSAFE_replayCanvasfalse是否回放 canvas 元素。启用后会向回放 iframe 添加 allow-scripts,从而脱离沙箱的脚本执行保护,这是不安全的。
pauseAnimationtrue回放器暂停时是否同时暂停 CSS 动画
mouseTailtrue是否在回放中显示鼠标拖尾。设为 false 可禁用鼠标拖尾。完整配置参见此类型定义
unpackFn-参见存储优化配方
logConfig-控制台输出回放的配置,参见控制台配方
plugins[]加载插件以提供扩展的回放功能。什么是插件?
useVirtualDomtrue跳转到新时间点时是否使用 Virtual DOM 优化
loggerconsole回放器用于打印警告或错误的 logger 对象

使用 rrweb-player

由于 @rrweb/replayReplayer 只提供基础 UI,你可以选择 rrweb-player,它基于 rrweb 的公开 API 构建,提供功能更丰富的回放器 UI。

安装

打包器 / npm(推荐):

shell
npm install rrweb-player
js
import rrwebPlayer from 'rrweb-player';
import 'rrweb-player/dist/style.css';

不使用打包器的浏览器环境(ESM):

html
<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 回退方案):

html
<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>
用法
js
new rrwebPlayer({
  target: document.body, // customizable root element
  props: {
    events,
  },
});
选项
默认值描述
events[]用于回放的事件
width1024回放器的宽度
height576回放器的高度
maxScale1回放器的最大缩放比例(1 = 100%),设为 0 表示不限制
autoPlaytrue是否自动播放
speedOption[1, 2, 4, 8]UI 中的速度选项
showControllertrue是否显示控制器 UI
tags{}使用键值对映射自定义各类自定义事件的样式
...-其他所有 Replayer 选项都会被透传

事件

开发者可能希望扩展回放器或响应其事件,例如在开始跳过无交互时间时通知用户。 Replayer 暴露了公开 API on,开发者可以用它监听事件并自定义行为:

js
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,然后刷新该静态文件,查看并调试最新代码对回放的影响。