rrweb入门

rrweb

背景

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

  • rrweb事例展示
  • 流程图

image.png

意义解决问题

  • 用户分析(常规的指标数据,只能做到一个统计。如果能通过录屏,我们能完整分析某个客户的行为。)

  • 重现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);},
});

image.png

回放

通过 rrweb.Replayer 可回放视频,需要传递录制好的数据。

// 2.回放
const replayer = new rrweb.Replayer(events);
replayer.play();

原理透析

基本概念

image.png

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
}

这个序列化的结果中有两点需要注意:

  1. 我们遍历 DOM 树时是以 Node 为单位,因此除了场景的元素类型节点以为,还包括 Text Node、Comment Node 等所有 Node 的记录。
  2. 我们给每一个 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变化做了记录)

之所以说我们的序列化方法是非标准的是因为我们还需要做以下几部分的处理:

  1. 去脚本化。被录制页面中的所有 JavaScript 都不应该被执行,例如我们会在重建快照时将 script 标签改为 noscript 标签,此时 script 内部的内容就不再重要,录制时可以简单记录一个标记值而不需要将可能存在的大量脚本内容全部记录。
  2. 记录没有反映在 HTML 中的视图状态。例如 <input > 输入后的值不会反映在其 HTML 中,而是通过 value 属性记录,我们在序列化时就需要读出该值并且以属性的形式回放成 。
  3. 相对路径转换为绝对路径。回放时我们会将被录制的页面放置在一个<iframe>中,此时的页面 URL为重放页面的地址,如果被录制页面中有一些相对路径就会产生错误,所以在录制时就要将相对路径进行转换,同样的 CSS 样式表中的相对路径也需要转换。
  4. 尽量记录 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”,可以防止任何脚本的执行。

  1. 高精度计时器
  2. 补全缺失节点
  3. 模拟hover
  4. 从任意时间开始播放

高精度计时器

之所以强调回放所⽤的计时器是⾼精度的,是因为原⽣的 setTimeout 并不能保证在设置的延迟时间之后准确执⾏,例如主线程阻塞时就会被推迟。

对于我们的回放功能⽽⾔,这种不确定的推迟是不可接受的,可能会导致各种怪异现象的发⽣,因此我们通过 requestAnimationFrame(根据设备的屏幕刷新率来调整动画的帧率) 来实现⼀个不断校准的定时器,确保绝⼤部分情况下操作的重放延迟不超过⼀帧。

同时⾃定义的计时器也是我们实现“快进”功能的基础。

补全缺失节点

rrweb 中,当进行页面重放过程中,如果发现了缺失的节点,它会通过补全缺失节点的方式来还原页面的完整状态。

页面重放的过程是通过按照操作的记录序列逐步还原页面状态的。记录的操作序列包括了用户在页面上执行的各种操作,比如点击、输入等。这些操作会导致页面上的节点发生相应的变化,包括添加、删除、修改等。

然而,在记录操作序列的过程中,可能会发生节点变化的时机比较复杂的情况,比如动态插入节点、使用 Shadow DOM、异步加载等。这些情况会导致在记录过程中无法捕获到节点变化的信息,导致记录的操作序列中缺失了节点的变化信息。

为了解决这个问题,rrweb 会使用 MutationObserver 监听页面上节点的变化。当发现有节点被添加或删除时,rrweb 会将这些节点的信息记录下来,并结合之前记录的操作序列进行分析。通过分析节点的变化情况,以及操作序列中的操作类型和位置rrweb 可以推断出缺失的节点应该是什么,并进行补全。

模拟 Hover

从任意时间点开始播放

除了基础的回放功能之外,我们还希望 rrweb-player 这样的播放器可以提供和视频播放器类似的功能,如拖拽到进度条至任意时间点播放。

实际实现时我们通过给定的起始时间点将快照链分为两部分,分别是时间点之前和之后的部分。然后同步执行之前的快照链,再正常异步执行之后的快照链就可以做到从任意时间点开始播放的效果。

播放器的进度条是如何控制与每个增量快照发生的时间对应上呢?

比如在播放时用户点击进度条上的某一点,这一点距离初始时间点是 timeOffset 长度,点击的这个点可以叫做基线时间点 baselineTime,rrweb 会根据这个点将所有的事件分成两部分:前一部分是在基线时间点前已经发生的事件队列,后一部分是待回放的事件队列。把前一部分事件同步还原构建完成,作为后面队列的全量基准 DOM 树,再继续异步地按照正确的时间间隔构建后面的增量快照。

问题扩展

如何将rrweb 转化为视频

  • rrvideo
    puppeteer 在服务端运行无头浏览器,在无头浏览器中回放录制的数据,然后每秒截取一定数量的图片,最后通过 ffmpeg 合成视频。下面是大致的流程图

image.png

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/74415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【鲁棒电力系统状态估计】基于投影统计的电力系统状态估计的鲁棒GM估计器(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringCloud Alibaba 入门到精通 - Nacos

SpringCloud Alibaba 常用组件 一、基础结构搭建1.父工程创建2.子工程创建 二、Nacos&#xff1a;注册中心1.服务端搭建2.注册中心-客户端搭建3.注册中心-管理页面4.注册中心-常用配置5.注册中心-核心功能总结 三、Nacos注册中心集成Load Balancer 、OpenFeign1.Nacos客户端集成…

一键部署k8s集群

前置动作 关闭防火墙 systemctl disable firewalld && systemctl stop firewalld 关闭SELinux sed -i s#SELINUXenforcing#SELINUXdisabled#g /etc/selinux/config && grep SELINUXdisabled /etc/selinux/config setenforce 0 getenforce 关闭swap # 关闭…

前端面试题JS篇(4)

浏览器缓存 浏览器缓存分为强缓存和协商缓存&#xff0c;当客户端请求某个资源时&#xff0c;获取缓存的流程如下&#xff1a; 先根据这个资源的一些 http header 判断它是否命中强缓存&#xff0c;如果命中&#xff0c;则直接从本地获取缓存资源&#xff0c;不会发请求到服务…

c语言练习44:深入理解strstr

深入理解strstr strstr作用展示&#xff1a; #include <stdio.h> #include <string.h> int main() {char str[] "This is a simple string";char* pch;pch strstr(str, "simple");/*strncpy(pch, "sample", 6);*/printf("%s…

Android逆向学习(一)vscode进行android逆向修改并重新打包

Android逆向学习&#xff08;一&#xff09;vscode进行android逆向修改并重新打包 写在前面 其实我不知道这个文章能不能写下去&#xff0c;其实我已经开了很多坑但是都没填上&#xff0c;现在专利也发出去了&#xff0c;就开始填坑了&#xff0c;本坑的主要内容是关于androi…

Scrum认证高级Scrum Master (A-CSM) 认证培训课程

课程简介 高级ScrumMaster (Advanced Certified ScrumMaster, A-CSM) 认证课程是国际Scrum联盟推出的进阶级Scrum认证课程&#xff0c;是Scrum Master通往专业级敏捷教练必经的学习路径。 在ScrumMaster&#xff08;CSM&#xff09;认证课程中&#xff0c;您学习到了Scrum的价…

Centos 6.5 升级到Centos7指导手册

一、背景 某业务系统因建设较早&#xff0c;使用的OS比较过时&#xff0c;还是centos6.5的系统&#xff0c;因国产化需要&#xff0c;需将该系统升级到BClinux 8.6&#xff0c;但官方显示不支持centos 6.x升级到8&#xff0c;需先将centos6.5升级到centos7的最新版&#xff0c…

华为OD机试 - 战场索敌 - 深度优先搜索dfs算法(Java 2023 B卷 100分)

目录 一、题目描述二、输入描述三、输出描述四、深度优先搜索dfs五、解题思路六、Java算法源码七、效果展示1、输入2、输出3、说明4、如果增加目标敌人数量K为55、来&#xff0c;上强度 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 一、题目描述 有一个大小是N*M…

RNA 36. SCI 文基于转录组数据识别CNV并可视化 (CaSpER)

转录组生信分析教程 桓峰基因公众号推出转录组分析教程&#xff0c;有需要生信的老师可以联系我们&#xff01;转录分析教程整理如下&#xff1a; RNA 1. 基因表达那些事--基于 GEO RNA 2. SCI文章中基于GEO的差异表达基因之 limma RNA 3. SCI 文章中基于T CGA 差异表达基因之 …

python28种极坐标绘图函数总结

文章目录 基础图误差线等高线polar场图polar统计图非结构坐标图 &#x1f4ca;python35种绘图函数总结&#xff0c;3D、统计、流场&#xff0c;实用性拉满 matplotlib中的画图函数&#xff0c;大部分情况下只要声明坐标映射是polar&#xff0c;就都可以画出对应的极坐标图。但…

基于SSM的校园美食交流系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

2023-9-10 集合-Nim游戏

题目链接&#xff1a;集合-Nim游戏 #include <iostream> #include <cstring> #include <algorithm> #include <unordered_set>using namespace std;const int N 110, M 10010;int n, m; int s[N], f[M];int sg(int x) {if(f[x] ! -1) return f[x];//…

【补】代码随想录算法训练营day38|动态规划 |509. 斐波那契数|70. 爬楼梯|746. 使用最小花费爬楼梯

动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff0c;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的&#xff0c;这一点就区分于贪心&#xff0c;贪心没有状态推…

【初阶C语言】操作符1--对二进制的操作

前言&#xff1a;本节内容介绍的操作符&#xff0c;操作的对象是二进制位。所以前面先介绍整数的二进制位 一、二进制位介绍 1.二进制介绍 &#xff08;1&#xff09;整数的二进制表示形式有三种&#xff1a;原码、反码和补码。 &#xff08;2&#xff09;原码、反码和补码的…

conda的使用教程

conda的介绍 简单来说&#xff0c;conda软件就是来管理包的软件。以Python为例&#xff0c;在实际生活中&#xff0c;我们要处理多个不同的项目&#xff0c;因此&#xff0c;要安装不同的项目所需要的包&#xff0c;为了管理方便&#xff0c;conda就是用来打理不同项目的包&…

Kafka3.0.0版本——消费者(消费者组详细消费流程图解及消费者重要参数)

目录 一、消费者组详细消费流程图解二、消费者的重要参数 一、消费者组详细消费流程图解 创建一个消费者网络连接客户端&#xff0c;主要用于与kafka集群进行交互&#xff0c;如下图所示&#xff1a; 调用sendFetches发送消费请求&#xff0c;如下图所示&#xff1a; (1)、Fet…

什么是50ETF期权开户条件,怎么开期权交易权限?

50ETF期权是指上证50ETF期权&#xff0c;标的物是上证50ETF&#xff0c;代码是&#xff08;510500&#xff09;&#xff0c;期权是一种在上证50ETF基础上进行衍生品交易的金融工具&#xff0c;下文科普什么是50ETF期权开户条件&#xff0c;怎么开期权交易权限&#xff1f;本文来…

FFmpeg入门之FFmpeg源码编译

1.源码下载: git clone https://github.com/FFmpeg/FFmpeg.git windows : macos: ubuntu: 2.编译FFmpeg CompilationGuide – FFmpeg windows: 1.下载yasm并安装 : Download - The Yasm Modular Assembler Project 下载后复制到c:/windows 2.下载SDL 3.下载H264/265源码 git…

css画一条渐变的虚线

效果展示 原理&#xff1a;给元素设置一个渐变的背景色&#xff0c;画一条白色的虚线盖住背景&#xff0c;就达到了渐变虚线的效果 代码&#xff1a; <div class"pending-line"></div>.pending-line{width: 101px;border-top: 2px dashed #fff; // do…