rrweb入门
rrweb
背景
rrweb 是 record and replay the web,是当下很流行的一个录制屏幕的开源库。与我们传统认知的录屏方式(如 WebRTC)不同的是,rrweb 录制的不是真正的视频流,而是一个记录页面 DOM 变化的 JSON 数组,因此不能录制整个显示器的屏幕,只能录制浏览器的一个页签(录屏)。
- rrweb事例展示
- 流程图

意义解决问题
-
用户分析(常规的指标数据,只能做到一个统计。如果能通过录屏,我们能完整分析某个客户的行为。)
-
重现bug(客户说有bug,但是复线不了,环境不一样,数据不一样。我们只能推断,但是有了录屏,我们就能很好的还原现场,知道本质操作)
-
代替视频录制 (录制体积更⼩、清晰度⽆损的产品演⽰。(纯粹是html,不用装插件))
基本使用
安装
npm install rrweb
录制
通过 rrweb.record 方法来录制页面,emit 回调可接收到录制的数据。
import rrweb from 'rrweb';
// 1.录制
let events = []; // 记录快照rrweb.record({emit(event) {// 将 event 存入 events 数组中events.push(event);},
});

回放
通过 rrweb.Replayer 可回放视频,需要传递录制好的数据。
// 2.回放
const replayer = new rrweb.Replayer(events);
replayer.play();
原理透析
基本概念

rrweb-snapshot 快照的生成
将页面中的dom转化为可序列化的数据结构并添加唯一标识
例如以下的 DOM 树:
<html><body><header></header></body>
</html>
会被序列化成类似这样的数据结构:
{"type": "Document","childNodes": [{"type": "Element","tagName": "html","attributes": {},"childNodes": [{"type": "Element","tagName": "head","attributes": {},"childNodes": [],"id": 3},{"type": "Element","tagName": "body","attributes": {},"childNodes": [{"type": "Text","textContent": "\n ","id": 5},{"type": "Element","tagName": "header","attributes": {},"childNodes": [{"type": "Text","textContent": "\n ","id": 7}],"id": 6}],"id": 4}],"id": 2}],"id": 1
}
这个序列化的结果中有两点需要注意:
- 我们遍历 DOM 树时是以 Node 为单位,因此除了场景的元素类型节点以为,还包括 Text Node、Comment Node 等所有 Node 的记录。
- 我们给每一个 Node 都添加了唯一标识 id,这是为之后的增量快照做准备。
在完成一次全量快照之后,我们就需要基于当前视图状态观察所有可能对视图造成改动的事件,在 rrweb 中我们已经观察了以下事件(将不断增加,增量序列化):
-
DOM 变动
- 节点创建、销毁
- 节点属性变化
- 文本变化
-
鼠标移动
-
鼠标交互
- mouse up、mouse down
- click、double click、context menu
- focus、blur
- touch start、touch move、touch end
-
页面或元素滚动
-
视窗大小改变
-
输入
类似git ,先提交一个版本,每次再追加追加。
记录的方法: MutationObserver
- MutationObserver 是一个用于监听 DOM 变化的 JavaScript 接口。触发方式为批量异步回调,一系列dom 变化之后,通过其回调函数开始接收通知。MutationObserver 可以监听节点的添加、移除、属性变化等操作。
序列化中的特殊处理(只是对dom变化做了记录)
之所以说我们的序列化方法是非标准的是因为我们还需要做以下几部分的处理:
去脚本化。被录制页面中的所有 JavaScript 都不应该被执行,例如我们会在重建快照时将 script 标签改为 noscript 标签,此时 script 内部的内容就不再重要,录制时可以简单记录一个标记值而不需要将可能存在的大量脚本内容全部记录。记录没有反映在 HTML 中的视图状态。例如<input > 输入后的值不会反映在其 HTML 中,而是通过 value 属性记录,我们在序列化时就需要读出该值并且以属性的形式回放成 。相对路径转换为绝对路径。回放时我们会将被录制的页面放置在一个<iframe>中,此时的页面 URL为重放页面的地址,如果被录制页面中有一些相对路径就会产生错误,所以在录制时就要将相对路径进行转换,同样的 CSS 样式表中的相对路径也需要转换。尽量记录 CSS 样式表的内容。如果被录制页面加载了一些同源的 样式表,我们则可以获取到解析好的 CSS rules,录制时将能获取到的样式都 inline 化,这样可以让一些内网环境(如 localhost)的录制也有比较好的效果。
序列化
任何语言,数据都是由数据结构来表示的,我们将数据结构转换成二进制,字符串的过程就是序列化
rebuild
- 将snapshot 记录的数据结构重建为对应的DOM
rrweb-player
为rrweb 提供的一套UI 控件,提供基于GUI的
录制原理
MutationObserver
播放阶段
在序列化设计中我们提到了“去脚本化”的处理,即在回放时我们不应该执行被录制页面中的 JavaScript,在重建快照的过程中我们将所有 script 标签改写为 noscript 标签解决了部分问题。但仍有一些脚本化的行为是不包含在 script 标签中的,例如 HTML 中的 inline script、表单提交等。
脚本化的行为多种多样,如果仅过滤已知场景难免有所疏漏,而一旦有脚本被执行就可能造成不可逆的非预期结果。因此我们通过 HTML 提供的 iframe 沙盒功能进行浏览器层面的限制(隔离环境,嵌入其他应用,兼容性考虑)。
rrweb 的播放器是在一个 iframe 上回放录屏的,为了阻断 iframe 上的用户交互需要做一些特殊处理,比如在 iframe 标签上设置 CSS 属性:
pointer-events: none;
为了去脚本化,将 <script> 标签替换为 <noscript> 标签,另外将 iframe 的 sandbox 属性设置为 “allow-same-origin”,可以防止任何脚本的执行。
- 高精度计时器
- 补全缺失节点
- 模拟hover
- 从任意时间开始播放
高精度计时器
之所以强调回放所⽤的计时器是⾼精度的,是因为原⽣的 setTimeout 并不能保证在设置的延迟时间之后准确执⾏,例如主线程阻塞时就会被推迟。
对于我们的回放功能⽽⾔,这种不确定的推迟是不可接受的,可能会导致各种怪异现象的发⽣,因此我们通过 requestAnimationFrame(根据设备的屏幕刷新率来调整动画的帧率) 来实现⼀个不断校准的定时器,确保绝⼤部分情况下操作的重放延迟不超过⼀帧。
同时⾃定义的计时器也是我们实现“快进”功能的基础。
补全缺失节点
在 rrweb 中,当进行页面重放过程中,如果发现了缺失的节点,它会通过补全缺失节点的方式来还原页面的完整状态。
页面重放的过程是通过按照操作的记录序列逐步还原页面状态的。记录的操作序列包括了用户在页面上执行的各种操作,比如点击、输入等。这些操作会导致页面上的节点发生相应的变化,包括添加、删除、修改等。
然而,在记录操作序列的过程中,可能会发生节点变化的时机比较复杂的情况,比如动态插入节点、使用 Shadow DOM、异步加载等。这些情况会导致在记录过程中无法捕获到节点变化的信息,导致记录的操作序列中缺失了节点的变化信息。
为了解决这个问题,rrweb 会使用 MutationObserver 监听页面上节点的变化。当发现有节点被添加或删除时,rrweb 会将这些节点的信息记录下来,并结合之前记录的操作序列进行分析。通过分析节点的变化情况,以及操作序列中的操作类型和位置,rrweb 可以推断出缺失的节点应该是什么,并进行补全。
模拟 Hover
从任意时间点开始播放
除了基础的回放功能之外,我们还希望 rrweb-player 这样的播放器可以提供和视频播放器类似的功能,如拖拽到进度条至任意时间点播放。
实际实现时我们通过给定的起始时间点将快照链分为两部分,分别是时间点之前和之后的部分。然后同步执行之前的快照链,再正常异步执行之后的快照链就可以做到从任意时间点开始播放的效果。
播放器的进度条是如何控制与每个增量快照发生的时间对应上呢?

比如在播放时用户点击进度条上的某一点,这一点距离初始时间点是 timeOffset 长度,点击的这个点可以叫做基线时间点 baselineTime,rrweb 会根据这个点将所有的事件分成两部分:前一部分是在基线时间点前已经发生的事件队列,后一部分是待回放的事件队列。把前一部分事件同步还原构建完成,作为后面队列的全量基准 DOM 树,再继续异步地按照正确的时间间隔构建后面的增量快照。
问题扩展
如何将rrweb 转化为视频
- rrvideo
puppeteer 在服务端运行无头浏览器,在无头浏览器中回放录制的数据,然后每秒截取一定数量的图片,最后通过 ffmpeg 合成视频。下面是大致的流程图


相关文章:
rrweb入门
rrweb 背景 rrweb 是 record and replay the web,是当下很流行的一个录制屏幕的开源库。与我们传统认知的录屏方式(如 WebRTC)不同的是,rrweb 录制的不是真正的视频流,而是一个记录页面 DOM 变化的 JSON 数组&#x…...
OSCP系列靶场-Esay-Vegeta1保姆级
OSCP系列靶场-Esay-Vegeta1保姆级 目录 OSCP系列靶场-Esay-Vegeta1保姆级总结准备工作信息收集-端口扫描目标开放端口收集目标端口对应服务探测 信息收集-端口测试22-SSH端口的信息收集22-SSH端口版本信息与MSF利用22-SSH协议支持的登录方式22-SSH手动登录尝试(无)22-SSH弱口令…...
AJAX学习笔记9 搜索联想自动补全
AJAX学习笔记8 跨域问题及解决方案_biubiubiu0706的博客-CSDN博客 其实就一个功能 搜索联想 自动补全 键盘按下事件keydown 键盘弹起事件keyup 做模糊查询 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><t…...
Docker启动失败报错Failed to start Docker Application Container Engine解决方案
Docker启动失败报错Failed to start Docker Application Container Engine解决方案_天涯人6的博客-CSDN博客...
安装React脚手架
在安装React脚手架之前,你需要决定使用哪个包管理工具。这里我们选择使用npm。运行下面的命令来安装React脚手架: npm install -g create-react-app这个命令会在全局安装React脚手架工具create-react-app。 现在,你已经准备好创建一个新的R…...
想要精通算法和SQL的成长之路 - 受限条件下可到达节点的数目
想要精通算法和SQL的成长之路 - 受限条件下可到达节点的数目 前言一. 相交链表(邻接图和DFS) 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 相交链表(邻接图和DFS) 原题链接 public int reachableNodes(int n, int[][] ed…...
加快项目开发进度常用5种方法
项目进度管理是根据进度目标,制定合理的进度计划,全程监控项目进度的执行情况。这样有利于明确项目目标,协调团队行动,提高开发效率,从而最大化项目利益。而加快项目进度,有利于提高项目整体效率࿰…...
leetcode做题笔记136. 只出现一次的数字
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。 思路一:快排(…...
vuex 模拟异步调用
在页面中首先定义一个调用vuex异步函数的方法 <el-button click fetchData></el-button> {{asyncData}} vuex 中 const store new Vuex.Store({state: {asyncData: null,},mutations: {SET_ASYNC_DATA(state, data) {state.asyncData data;},},actions: {fe…...
error:Failed building wheel for XXX
解决方案适用于大多数的pip 安装时出现的Failed building wheel for XXX 出现问题 按以往快速安装包的经验,第一反应当然是使用简单又快捷的terminal命令加上镜像,如下: pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple结…...
【linux命令讲解大全】112.Linux 系统管理工具:dpkg-statoverride 和 dstat 的使用介绍
文章目录 dpkg-statoverride补充说明语法选项实例 dstat补充说明下载安装方法一方法二 使用说明语法常用选项实例 从零学 python dpkg-statoverride 在 Debian Linux 中覆盖文件的所有权和模式。 补充说明 dpkg-statoverride 命令用于在 Debian Linux 中覆盖文件的所有权和模…...
ffmpeg草稿
avformat 用于解析容器和协议(protocol)。 avcodec 用于编码和解码基本流(您已经拥有的)。 Qt/C音视频开发44-本地摄像头推流(支持分辨率/帧率等设置/实时性极高)_feiyangqingyun的博客-CSDN博客 void FFmpegThread::initInputFormat() {//本地摄像头/桌…...
熵 | 无线通信知识
文章目录 一、信息论(熵、联合熵、条件熵)二、Bernoulli熵三、联合熵和条件熵四、互信息五、相对熵(KL距离)六、微分熵七、最大熵分布常需要的不等式公式 一、信息论(熵、联合熵、条件熵) 熵定义: H ( X ) E [ − l …...
黑马JVM总结(七)
(1)StringTable_编译器优化 “a”“b”对应#4:是去常量池中找ab的这个符号 astore 5:是把这个存入编号为5的局部变量 “ab”对应的指令 #4,跟“a”“b”对应#4下面弄是一样的 在执行s3“ab”这行个代码时…...
Vue3核心语法一
Vue3核心语法一 rectiveshallowReactiverefcomputedwatchwatchEffet 使用Vue3创建项目template中标签可以多个根标签,可以通过setup开启组合式API,组合式API优点可以使相同业务放到一起 rective 定义响应式数据, import { reactive} from "vue";const data reactiv…...
5.11.Webrtc接口的设计原理
在上节课中呢,我向你介绍了web rtc的接口宏,那有很多同学会产生疑问啊,那觉得web rtc为什么要把接口设计的这么复杂?还非要通过宏来实现一个代理类,再通过代理类来调用到web rtc内部。 那为什么要这么设计呢…...
2022年09月 C/C++(八级)真题解析#中国电子学会#全国青少年软件编程等级考试
C/C++编程(1~8级)全部真题・点这里 第1题:道路 N个以 1 … N 标号的城市通过单向的道路相连:。每条道路包含两个参数:道路的长度和需要为该路付的通行费(以金币的数目来表示) Bob and Alice 过去住在城市 1.在注意到Alice在他们过去喜欢玩的纸牌游戏中作弊后,Bob和她分手…...
Vue3 监听属性-watch
文章目录 Vue3 监听属性-watch1. 概念2. 实例2.1 通过使用 watch 实现计数器2.2 千米与米之间的换算2.3 异步加载中使用 watch2.4 小计 Vue3 监听属性-watch 1. 概念 Vue3 监听属性 watch,可以通过 watch 来响应数据的变化。 watch 的作用:用于监测响应…...
JWT安全
文章目录 理论知识cookie(放在浏览器)session(放在 服务器)tokenjwt(json web token)headerpayloadSignatureJWT通信流程 JWT与Token 区别相同点区别 WebGoat靶场--JWT tokens环境启动第四关第五关第七关 属于越权漏洞 理论知识 cookie(放在浏览器) …...
LabVIEW利用人工神经网络辅助进行结冰检测
LabVIEW利用人工神经网络辅助进行结冰检测 结冰对各个领域构成重大威胁,包括但不限于航空航天和风力涡轮机行业。在起飞过程中,飞机机翼上轻微积冰会导致升力降低25%。研究报告称,涡轮叶片上的冰堆积可在19个月的运行时间内造成29MWh的功率损…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...
Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...
