学会Rrweb前端可回溯方案,再也不怕客诉了

6,131 阅读5分钟

rrweb 是 'record and replay the web' 的简写,用来录制并回放任意 web 界面中的用户操作。

一, 使用场景

  • 记录用户使用产品的方式并加以分析,进一步优化;
  • 采集用户遇到的bug的操作路径,予以复现;
  • 回溯用户的行为,可作为证据存储;
  • 录制体积更小,清晰度无损的演示视频;
  • 记录自动化测试的执行情况;

二, 新思路:dom快照

image.png 简单理解: ⻚⾯中的视图状态可以通过 DOM 树的形式描述,所以当我们尝试录制⼀个⻚⾯时,我们可以记录 DOM 树在各个时间点上的状态。 记录每一时刻页面的DOM状态,回放的时候根据时间点显示即可。

问题: DOM数据很庞大,每时每刻都在记录,有点不合适。

三, 实现原理,Rrweb采用的性能优化方案

  • 记录初始页面的DOM 状态,或者特定某个时刻的DOM 状态, 后续收集的是不同时间点的操作指令 或者 某个时刻 某个DOM 的变化作为一个增量快照, 在原先快照的基础上,不断加入根据行为解析的DOM 数据,构建了后续的快照,减少大量数据的存储或传输。

image.png

  • 节流,对于鼠标移动,页面滚动事件进行了节流
  • 压缩数据,尝试过用 pako.js 进行压缩

四,RRWEB项目结构

rrweb 主要由 3 部分组成:

  • rrweb-snapshot,包含 snapshot 和 rebuild 两个功能。 snapshot 用于将 DOM 及其状态转化为可序列化的数据结构并添加唯一标识; rebuild 则是将 snapshot 记录的数据结构重建为对应的 DOM,并插入文档中
  • rrweb,包含 record 和 replay 两个功能。 record 用于记录 DOM 中的所有变更(mutation); replay 则是将记录的变更按照对应的时间一一重放。
  • rrweb-player,为 rrweb 提供一套 UI 控件,提供基于 图形用户界面的暂停、快进、拖拽至任意时间点播放等功能。

五,rrweb 技术细节

拆分结构

  • snapshot 方法
  1. 第一点:Snapshot构建页面 DOM 树,同时生成了 id -> Node 的映射,即在构建 DOM 树时为每个节点生成一个唯一的id, 同时根据 id 生成一份映射,这个映射只要是为了方便后续的增量快照操作
  2. 第二点: 将href,src,CSS中的相对路径设为绝对路径 将一些脚本,样式,图片等引用的相对路径改为绝对路径
  3. 第三点: 将页面引用的样式变为内联样式,以确保可以使用本地样式 将页面引用的样式读取变为内联样式
  4. 第四点:将一些DOM状态内联到HTML属性中,例如HTMLInputElement的值 记录没有反映在 HTML 中的视图状态。例如 输⼊后的值不会反映在其 HTML中,我们需要读取其 value 值并加以记录
  5. 第五点:将script标记转换为noscript标记,以避免脚本被执行 在播放录制页面时,页面的脚本是不能够被执行的,需要禁掉
  • rebuild方法
  1. 通过创建Dom, 设置属性等,并且将对应的DOM 插入文档中
  • record 方法
  1. 前面说到增量快照,那增量数据是怎么收集的呢?开始录制之后,会针对当前页面生成一个DOM 快照,然后开始监听用户操作和页面DOM的变化。

监听行为如下:

  • DOM 变动 o 节点创建、销毁 o 节点属性变化 o 文本变化
  • 鼠标交互 o mouse up、mouse down o click、double click、context menu o focus、blur o touch start、touch move、touch end
  • 页面或元素滚动
  • 视窗大小改变
  • 鼠标移动
  • 输入

监听的方式有:

  • DOM变动,MutationObserver 其中,监听 DOM 变化 主要是通过 API --- MutationObserver 来实现。当监视的DOM 发生变动时, MutationObserver 将收到通知并触发预先设定好的回调参数,与 addEventListener 方法比较相似
  • 鼠标交互,页面滚动,视窗大小这些则通过 事件绑定的形式去监听 通过 的形式监听

image.png

  • 鼠标移动 通过记录⿏标移动位置,我们可以在回放时模拟⿏标移动轨迹。保证回放时⿏标移动流畅的同时也要尽量减少对应 Oplog 的数量,所以我们会做两层节流处理。第⼀层是每 50 ms 最多记录⼀次⿏标坐标,第⼆层是每 500 ms 最多发送⼀次⿏标坐标集合,第⼆层的主要⽬的是避免⼀次请求内容过多⽽做的分段。
  • 输入 我们需要观察input, textarea, select 三种元素的输⼊,包含⼈为交互和程序设置两种途径的输⼊。
  1. 人为交互 对于⼈为交互的操作我们主要靠监听 input 和 change 两个事件观察,需要注意的是对不同事件但值相同的情况进⾏去重。
  2. 程序设置 通过代码直接设置这些元素的属性也不会触发事件,我们可以通过劫持对应属性的 setter 来达到监听的⽬的。

replay回放

在确定了最终录制⽅案之后,我们就可以实现对应的回放功能。相对来说回放的思路更为明确,可以分为以下 3 个主要步骤:

  • 在⼀个沙盒环境中将快照重建为对应的 DOM 树。
  • 将 Oplog 中的操作按照时间戳排列,放⼊⼀个操作队列中。
  • 启动⼀个计时器,不断检查操作队列,将到时间的操作取出重现。

六,使用方法

github.com/rrweb-io/rr…