• rrweb入门


    rrweb

    背景

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

    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);
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image.png

    回放

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

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

    原理透析

    基本概念

    image.png

    rrweb-snapshot 快照的生成

    将页面中的dom转化为可序列化的数据结构并添加唯一标识

    例如以下的 DOM 树:

    
      
        
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    会被序列化成类似这样的数据结构:

    {
      "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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

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

    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 中的视图状态。例如 输入后的值不会反映在其 HTML 中,而是通过 value 属性记录,我们在序列化时就需要读出该值并且以属性的形式回放成 。
    3. 相对路径转换为绝对路径。回放时我们会将被录制的页面放置在一个