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的功率损…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...