最近在作 web 端录制 和回放的时候遇到一个比较好的库 rrweb, 可是网上的资料相对比较少,撸了遍源码,作个小总结。本文阅读大概会了解到如下几个方面~node
rrweb 是什么git
适用场景github
录制回放实现原理web
使用 rrweb 应注意的问题数组
安全问题安全
rrweb 库组成部分 + 简介服务器
rrweb 各个模块对应的技术细节数据结构
web 端录制回放的一个基础库,即记录页面中的 DOM 结构还有用户操做行为,在远程实现回放。一图胜千言,可看下面的gif 图 app
这一块能够简单地理解为 “快照 + 操做指令”, 通常记录页面状态,咱们能够根据时间记录每一时刻页面的 DOM状态,回放的时候根据时间点显示便可,可是通常一个页面DOM 数据是很庞大的,以下图所示,这个仍是页面 DOM 比较少的状况 异步
所以 rrweb 采起了另一种方式,记录初始页面的DOM 状态,或者特定某个时刻的DOM 状态,后续收集的是不一样时间点的操做指令 或者 某个时刻 某个DOM 的变化做为一个增量快照,在原先快照的基础上,不断加入根据行为解析的DOM 数据,构建了后续的快照,减小大量数据的存储或传输。
除此之外,可本身对收集到的数据进行处理
关于数据安全问题,这个应该加密便可
一、rrweb-snapshot 提供了 snapshot, resbuid接口,snapshot遍历 页面DOM 返回当前页面 DOM 视图的一个序列化的数据结构, rebuild, 则解析特定的数据还原DOM, 并插入文档中 例如:
这个模块的功能主要有: a) snapshot 方法
第一点:Snapshot 经过 takeFullSnapShotg构建页面 DOM 树,同时生成了 id -> Node 的映射,即在构建 DOM 树时为每一个节点生成一个惟一的id, 同时根据 id 生成一份映射,这个映射只要是为了方便后续的增量快照操做
第二点: 将href,src,CSS中的相对路径设为绝对路径 将一些脚本,样式,图片等引用的相对路径改成绝对路径 好比:
那通过转换以后,回放时,图片的连接地址已经变为以前域名下的地址
第三点: 将页面引用的样式变为内联样式,以确保可使用本地样式 将页面引用的样式读取变为内联样,例如
除此之外,还有一种样式逻辑,即经过 CssStyleSheet.sheet.insertRule的形式插入页面的样式:
第四点:将一些DOM状态内联到HTML属性中,例如HTMLInputElement的值
记录没有反映在 HTML 中的视图状态。例如 输⼊后的值不会反映在其 HTML中,咱们须要读取其 value 值并加以记录
第五点:将script标记转换为noscript标记,以免脚本被执行 在播放录制页面时,页面的脚本是不可以被执行的,须要禁掉
b) rebuild方法 经过建立Dom, 设置属性等,而且将对应的DOM 插入文档中
二、Rrweb 提供了 record 和 replay 功能。 a) record 方法: 前面说到增量快照,那增量数据是怎么收集的呢?开始录制以后,会针对当前页面生成一个DOM 快照,而后开始监听用户操做和页面DOM的变化。 监听行为以下:
监听的方式有: (1)MutationObserver 其中,监听 DOM 变化 主要是经过 API --- MutationObserver 来实现。当监视的DOM 发生变更时, MutationObserver 将收到通知并触发预先设定好的回调参数,与 addEventListener 方法 比较类似 例如:
如上图所示,当咱们尝试改变页面 DOM 的属性,或者新增 DOM 节点的时候,都会对应生成一条 MutationObserver record, record 记录了一些变更信息~ 在 rrweb 中, 对每一条mutation record 作了几下处理
针对不一样的类型进行处理, characterData 是节点内容或节点文本变更,attributes 是节点属性的变更,childList 是子节点的变更,包括新增子节点,移除子节点,移动子节点等。
新增节点的逻辑 在初始记录时,生成了页面快照同时维护一个 id -> Node 的映射, 所以当出现新增节点时,无需从新完整地生成一份快照,而只须要将新节点序列化并加入映射中便可。 因为MutationObserver触发方式为批量异步回调,具体来讲就是会在一系列 DOM 变化发生以后将这些变化一次性回调,传出的是一个 mutation 记录数组,那在序列化的时候,会存在重复记录的问题 例如如下例子中
如下两种方式均可以生成这种DOM 结构
一、建立节点 n1 并 append 在 body 中,再建立节点 n2 并 append 在 n1 中。 二、建立节点 n一、n2,将 n2 append 在 n1 中,再将 n1 append 在 body 中。
MutationObserver 对这两种Dom 操做方式的输出记以下图所示
第一种方式 record 记录
这种状况下虽然 n1 append 时尚未子节点,可是因为上述的批量异步回调机制,当咱们处理 mutation 记录时获取到的 n1 是已经有子节点 n2 的状态
第二种方式 record 记录
在处理序列化的过程当中,在处理新增节点时必须遍历其全部子孙节点,才能保证全部新增节点都被记录,可是这一策略应用在第一种状况中就会致使 n2 被做为新增节点记录两次,回放时就会产生与原页面不一致的 DOM 结构,所以,为避免这种状况, rrweb 中采起了‘惰性’新增节点,即在遍历 mutation record 时,不会立马去序列化新增的节点,而是收集新增的节点,在遍历完mutation 全部记录的时候再统一去重,序列化新增节点。
删除节点的逻辑
(2)鼠标移动,鼠标交互,页面滚动,视窗大小这些则经过 事件绑定的形式去监听,以下面页面滚动的监听
on 方法也是经过 addEventListener 的形式监听
b) replay 解析收集到的events 集合,进行还原 收集到的事件类型有
第一次阅读源码,写分析,可能比较粗糙,如有错误之处,欢迎指正。也欢迎一块儿交流,相关的问题也能够到github 上向做者提问哦,做者响应问题速度也是挺快的。