Files
rrweb/guide.zh_CN.md
Justin Halsall 2887c8c7e5 #853 Second try: fast-forward implementation v2: virtual dom optimization (#895)
* rrdom: add a diff function for properties

* implement diffChildren function and unit tests

* finish basic functions of diff algorithm

* fix several bugs in the diff algorithm

* replace the virtual parent optimization in applyMutation()

* fix: moveAndHover after the diff algorithm is executed

* replace virtual style map with rrdom

cssom version has to be above 0.5.0 to pass virtual style tests

* fix: failed virtual style tests in replayer.test.ts

* fix: failed polyfill tests caused by nodejs compatibility of different versions

* fix: svg viewBox attribute doesn't work

Cause the attribute viewBox is case sensitive, set value for viewbox doesn't work

* feat: replace treeIndex optimization with rrdom

* fix bug of diffProps and disable smooth scrolling animation in fast-forward mode

* feat: add iframe support

* fix: @rollup/plugin-typescript build errors in rrweb-player

Error: @rollup/plugin-typescript TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'

* fix: bug when fast-forward input events and add test for it

* add test for fast-forward scroll events

* fix: custom style rules don't get inserted into some iframe elements

* code style tweak

* fix: enable to diff iframe elements

* fix  the jest error "Unexpected token 'export'"

* try to fix build error of rrweb-player

* correct the attributes definition in rrdom

* fix: custom style rules are not inserted in some iframes

* add support for shadow dom

* add support for MediaInteraction

* add canvas support

* fix unit test error in rrdom

* add support for Text, Comment

* try to refactor RRDom

* refactor RRDom to reduce duplicate code

* rename document-browser to virtual-dom

* increase the test coverage for document.ts and add ownerDocument for it

* Merge branch 'master' into virtual-dom

* add more test for virtual-dom.ts

* use cssstyle in document-nodejs

* fix: bundle error

* improve document-nodejs

* enable to diff scroll positions of an element

* rename rrdom to virtualDom for more readability and make the tree public

* revert unknown change

* improve the css style parser for comments

* improve code style

* update typings

* add handling for the case where legacy_missingNodeRetryMap is not empty

* only import types from rrweb into rrdom

* Apply suggestions from code review

Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>

* Apply suggestions from code review

* fix building error in rrweb

* add a method setDefaultSN to set a default value for a RRNode's __sn

* fix rrweb test error and bump up other packages

* add support for custom property of css styles

* add a switch for virtual-dom optimization

* Apply suggestions from code review

1. add an enum type for NodeType
2. rename nodeType from rrweb-snapshot to RRNodeType
3. rename notSerializedId to unserializedId
4. add comments for some confusing variables

* adapt changes of #865 to virtual-dom and improve the test case for more coverage

* apply review suggestions

https://github.com/rrweb-io/rrweb/pull/853#pullrequestreview-922474953

* tweak the diff algorithm

* add description of the flag useVirtualDom and remove outdated logConfig

* Remove console.log

* Contain changes to document

* Upgrade rollup to 2.70.2

* Revert "Upgrade rollup to 2.70.2"

This reverts commit b1be81a2a76565935c9dc391f31beb7f64d25956.

* Fix type checking rrdom

* Fix typing error while bundling

* Fix tslib error on build

Rollup would output the following error:
`semantic error TS2343: This syntax requires an imported helper named '__spreadArray' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.`

* Increase memory limit for rollup

* Use esbuild for bundling

Speeds up bundling significantly

* Avoid circular dependencies and import un-bundled rrdom

* Fix imports

* Revert back to pre-esbuild

This reverts the following commits:
b7b3c8dbaa551a0129da1477136b1baaad28e6e1
72e23b8e27f9030d911358d3a17fe5ad1b3b5d4f
85d600a20c56cfa764cf1f858932ba14e67b1d23
61e1a5d323212ca8fbe0569e0b3062ddd53fc612

* Set node to lts (12 is no longer supported)

* Speed up bundling and use less memory

This fixes the out of memory errors happening while bundling

* remove __sn from rrdom

* fix typo

* test: add a test case for StyleSheet mutation exceptions while fast-forwarding

* rename Array.prototype.slice.call() to Array.from()

* improve test cases

* fix: PR #887 in 'virtual-dom' branch

* apply justin's suggestion on 'Array.from' refactor

related commit 0f6729d27a323260b36fbe79485a86715c0bc98a

* improve import code structure

Co-authored-by: Yun Feng <yun.feng@anu.edu.au>
2026-04-01 12:00:00 +08:00

22 KiB
Raw Blame History

使用指南

除通用的使用指南外,你可能还想通过场景示例了解特定场景下的使用方式,或是通过设计文档深入 rrweb 的技术细节。

安装

直接通过 <script> 引入

推荐通过 jsdelivr 的 CDN 安装:

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>

也可以在 URL 中指定具体的版本号,例如:

<script src="https://cdn.jsdelivr.net/npm/rrweb@0.7.0/dist/rrweb.min.js"></script>

仅引入录制部分

rrweb 代码分为录制和回放两部分,大多数时候用户在被录制的应用中只需要引入录制部分代码,同样可以通过 CDN 安装:

<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/record/rrweb-record.min.js"></script>

其它按需引入方式

除了仅包含录制代码的 record/rrweb-record-min.js 之外rrweb 还提供了其它多种可选的打包文件。所有包含 .min 的文件为同名文件的压缩版。

# 包含录制、回放、压缩数据、解压缩数据
rrweb-all.js
rrweb-all.min.js
# 包含录制、回放
rrweb.js
rrweb.min.js
# 回放所需的样式文件
rrweb.min.css
# 录制
record/rrweb-record.js
record/rrweb-record.min.js
# 压缩数据
record/rrweb-record-pack.js
record/rrweb-record-pack.min.js
# 回放
replay/rrweb-replay.js
replay/rrweb-replay.min.js
# 解压缩数据
replay/rrweb-replay-unpack.js
replay/rrweb-replay-unpack.min.js

通过 npm 引入

npm install --save rrweb

rrweb 同时提供 commonJS 和 ES modules 两种格式的打包文件,易于和常见的打包工具配合使用。

兼容性

由于使用 MutationObserver APIrrweb 不支持 IE11 以下的浏览器。可以从这里找到兼容的浏览器列表。

快速开始

录制

如果通过 <script> 的方式仅引入录制部分,那么可以访问到全局变量 rrwebRecord,它和全量引入时的 rrweb.record 使用方式完全一致,以下示例代码将使用后者。

rrweb.record({
  emit(event) {
    // 用任意方式存储 event
  },
});

rrweb 在录制时会不断将各类 event 传递给配置的 emit 方法,你可以使用任何方式存储这些 event 以便之后回放。

调用 record 方法将返回一个函数,调用该函数可以终止录制:

let stopFn = rrweb.record({
  emit(event) {
    if (events.length > 100) {
      // 当事件数量大于 100 时停止录制
      stopFn();
    }
  },
});

一个更接近实际真实使用场景的示例如下:

let events = [];

rrweb.record({
  emit(event) {
    // 将 event 存入 events 数组中
    events.push(event);
  },
});

// save 函数用于将 events 发送至后端存入,并重置 events 数组
function save() {
  const body = JSON.stringify({ events });
  events = [];
  fetch('http://YOUR_BACKEND_API', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body,
  });
}

// 每 10 秒调用一次 save 方法,避免请求过多
setInterval(save, 10 * 1000);

配置参数

rrweb.record(config) 的 config 部分接受以下参数

key 默认值 功能
emit 必填 获取当前录制的数据
checkoutEveryNth - 每 N 次事件重新制作一次全量快照
详见“重新制作快照”章节
checkoutEveryNms - 每 N 毫秒重新制作一次全量快照
详见“重新制作快照”章节
blockClass 'rr-block' 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见“隐私”章节
blockSelector null 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素
ignoreClass 'rr-ignore' 字符串或正则表达式,可用于自定义忽略元素的类名,详见“隐私”章节
maskTextClass 'rr-mask' 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见“隐私”章节
maskTextSelector null 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽
maskAllInputs false 将所有输入内容记录为 *
maskInputOptions { password: true } 选择将特定类型的输入框内容记录为 *
类型详见列表
maskInputFn - 自定义特定类型的输入框内容记录逻辑
maskTextFn - 自定义文字内容的记录逻辑
slimDOMOptions {} 去除 DOM 中不必要的部分
类型详见列表
inlineStylesheet true 是否将样式表内联
hooks {} 各类事件的回调
类型详见列表
packFn - 数据压缩函数,详见优化存储策略
sampling - 数据抽样策略,详见优化存储策略
recordCanvas false 是否记录 canvas 内容, 可用选项false, true
inlineImages false 是否将图片内容记内联录制
collectFonts false 是否记录页面中的字体文件
userTriggeredOnInput false 什么是 userTriggered
plugins [] 加载插件以获得额外的录制功能. 什么是插件?

隐私

页面中可能存在一些隐私相关的内容不希望被录制rrweb 为此做了以下支持:

  • 在 HTML 元素中添加类名 .rr-block 将会避免该元素及其子元素被录制,回放时取而代之的是一个同等宽高的占位元素。
  • 在 HTML 元素中添加类名 .rr-ignore 将会避免录制该元素的输入事件。
  • 所有带有.rr-mask类名的元素及其子元素的 text 内容将会被屏蔽。
  • input[type="password"] 类型的密码输入框默认不会录制输入事件。
  • 配置中还有更为丰富的隐私保护选项。

重新制作快照

默认情况下,要重放内容需要所有的 event如果你不想存储所有的 event可以使用checkout配置。

多数时候你不必这样配置。但比如在页面错误发生时,你只想捕获最近的几次 event ,这里有一个例子:

// 使用二维数组来存放多个 event 数组
const eventsMatrix = [[]];

rrweb.record({
  emit(event, isCheckout) {
    // isCheckout 是一个标识,告诉你重新制作了快照
    if (isCheckout) {
      eventsMatrix.push([]);
    }
    const lastEvents = eventsMatrix[eventsMatrix.length - 1];
    lastEvents.push(event);
  },
  checkoutEveryNth: 200, // 每 200 个 event 重新制作快照
});

// 向后端传送最新的两个 event 数组
window.onerror = function () {
  const len = eventsMatrix.length;
  const events = eventsMatrix[len - 2].concat(eventsMatrix[len - 1]);
  const body = JSON.stringify({ events });
  fetch('http://YOUR_BACKEND_API', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body,
  });
};

由于 rrweb 使用了增量快照机制,我们不能指定数量来捕获最近的几次 event。上面这个例子你可以拿到最新的 200-400 个 event 来发送给你的后端。

类似的,你可以通过配置 checkoutEveryNms 来捕获最近指定时间的 event :

// 使用二维数组来存放多个 event 数组
const eventsMatrix = [[]];

rrweb.record({
  emit(event, isCheckout) {
    // isCheckout 是一个标识,告诉你重新制作了快照
    if (isCheckout) {
      eventsMatrix.push([]);
    }
    const lastEvents = eventsMatrix[eventsMatrix.length - 1];
    lastEvents.push(event);
  },
  checkoutEveryNms: 5 * 60 * 1000, // 每5分钟重新制作快照
});

// 向后端传送最新的两个 event 数组
window.onerror = function () {
  const len = eventsMatrix.length;
  const events = eventsMatrix[len - 2].concat(eventsMatrix[len - 1]);
  const body = JSON.stringify({ events });
  fetch('http://YOUR_BACKEND_API', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body,
  });
};

在上面的例子中,你最终会拿到最新的 5-10 分钟的 event 来发送给你的后端。

回放

回放时需要引入对应的 CSS 文件:

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"
/>

再通过以下 JS 代码初始化 replayer

const events = YOUR_EVENTS;

const replayer = new rrweb.Replayer(events);
replayer.play();

使用 API 控制回放

const replayer = new rrweb.Replayer(events);

// 播放
replayer.play();

// 从第 3 秒的内容开始播放
replayer.play(3000);

// 暂停
replayer.pause();

// 暂停至第 5 秒处
replayer.pause(5000);

配置参数

可以通过 new rrweb.Replayer(events, options) 的方式向 rrweb 传递回放时的配置参数,具体配置如下:

key 默认值 功能
speed 1 回放倍速
root document.body 回放时使用的 HTML 元素
loadTimeout 0 加载异步样式表的超时时长
skipInactive false 是否快速跳过无用户操作的阶段
showWarning true 是否在回放过程中打印警告信息
showDebug false 是否在回放过程中打印 debug 信息
blockClass 'rr-block' 需要在回放时展示为隐藏区域的元素类名
liveMode false 是否开启直播模式
insertStyleRules [] 可以传入多个 CSS rule string用于自定义回放时 iframe 内的样式
triggerFocus true 回放时是否回放 focus 交互
UNSAFE_replayCanvas false 回放时是否回放 canvas 内容,开启后将会关闭沙盒策略,导致一定风险
pauseAnimation true 当播放器停止播放时,是否将 CSS 动画也停止播放
mouseTail true 是否在回放时增加鼠标轨迹。传入 false 可关闭,传入对象可以定制轨迹持续时间、样式等,配置详见类型
unpackFn - 数据解压缩函数,详见优化存储策略
plugins [] 加载插件以获得额外的回放功能. 什么是插件?
useVirtualDom true 在播放器跳转到一个新的时间点的过程中,是否使用 Virtual Dom 优化

使用 rrweb-player

rrweb 自带的回放只提供所有的 JS API 以及最基本的 UI如果需要功能更强的回放播放器 UI可以使用 rrweb-player。

安装

rrweb-player 同样可以使用 CDN 方式安装:

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>

或者通过 npm 安装:

npm install --save rrweb-player
import rrwebPlayer from 'rrweb-player';
import 'rrweb-player/dist/style.css';
使用

通过 props 传入 events 数据及配置项

new rrwebPlayer({
  target: document.body, // 可以自定义 DOM 元素
  // 配置项
  props: {
    events,
  },
});
配置项参数
key 默认值 功能
events [] 包含回放所需的数据
width 1024 播放器宽度
height 576 播放器高度
autoPlay true 是否自动播放
speedOption [1, 2, 4, 8] 倍速播放可选值
showController true 是否显示播放器控制 UI
tags {} 可以以 key value 的形式展示自定义事件在时间轴上的颜色
... - 其它所有 rrweb Replayer 的配置参数均可透传

事件

开发者可能希望监听回放时的各类事件,例如在跳过无用户操作的时间时给用户一些提示。

rrweb 的 Replayer 提供了 on API 用于提供该功能

const replayer = new rrweb.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 event
custom-event 回放自定义事件 event

使用 rrweb-player 时,也可以通过 addEventListener API 使用相同的事件功能,并且会获得 3 个额外的事件:

事件类型 描述
ui-update-current-time 当前回放时间点 { payload }
ui-update-player-state 当前回放状态 { payload }
ui-update-progress 当前回放百分比 { payload }

REPL 工具

在不安装 rrweb 的情况下,也可以通过使用 REPL 工具试用 rrweb 录制 web 应用。

运行 yarn repl,将会启动浏览器并在命令行要求输入一个测试的 url

Enter the url you want to record, e.g https://react-redux.realworld.io:

输入后等待浏览器打开指定页面,并在命令行中出现以下提示信息:

Enter the url you want to record, e.g https://react-redux.realworld.io: 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

该文件默认使用最新 bundle 的 rrweb 代码,所以我们可以在修改代码后运行 npm run bundle:browser,之后刷新静态文件就可以查看最新代码对回放的影响并进行调试。