基于rrweb框架对web 页面录制与回放

前言

最近来了个需求需要对web 页面上的操作行为做流程跟踪,并提供具体的数据,或者视频参照,作为合规的证据代码用户的操作轨迹,也避免一些纠纷。

对于web 页面的轨迹追踪,还需要知道操作行为。一想到这个第一想到的就是在线直播学习课程中,找个屏幕录播软件,讲解员打开该软件,开始在电脑上的一切行为都被录制成视频。然后就可以直接把视频分享给所有的人。而你再讲解过程中的一些列细节都能够播放出来。所以当初就想让原生APP开发一个这样类似的东西。再webview中进行播放。这样功能也就ok了。但是还是需要支持移动端web 网页的轨迹追踪。

解决问题方式

思路一:Canvas

利用Canvas截图,使用 html2canvas 库,不停的画页面然后不停的截图,再讲图片播放出来。后来调研了一下~ 发现这个可操作性几乎未零。太复杂了,数据量右大,而我们的页面又复杂,canvas 画半天耗内存,有时候会卡主。直接pass了

思路二:记录页面DOM变化

主要原理是:MutationObserver接口提供了监视对DOM树所做更改的能力。我们可以利用这个接口,保存每次变化的DOM数据,然后把这些数据转换成可视化的数据结构,然后给一个个保存起来。然后我们使用特定的方式对一个个保存起来的DOM数据进行还原并重新渲染出来。DOM节点的变化也就意味了页面轨迹发送了变化。这样就可以把这些轨迹记录下来。我们只需要再把这个还原出来的DOM播放的过程中进行录制,这样就可以保持下来这些轨迹视频。

进行实践

后续调研,以及其他同事的帮助,我们找到了LogRocket ,专业的web app用户行为记录工具,官网描述:

  • LogRocket记录用户在你的web上做的一切事情,以帮助你重现bug并更快的解决他们
  • 在你的web app中发现问题不应该如此艰难
  • 用LogRocket,重现问题就像他们发生在你自己的浏览器中一样

按照其描述,以及我们的进一步了解,发现LogRocket 里面的一些东西确实可以帮助我们解决问题。但是主要问题是还是因为LogRocket 这些数据,我们无法获取到自己的库里面。只能是第三方的库。并不满足我们的需求,还是就是要用它,需要花钱。
大家想深入的了解,可以查看官网 LogRocket 官网

后来我们又了解到一个开源的 rrweb 框架。rrweb主要由3部分组成:

  1. rrweb-snapshot,包括快照和重建功能。快照用于将DOM及其状态转换为具有唯一标识符的可序列化数据结构; 重建功能是将快照重建为相应的DOM。
  2. rrweb,包括两个功能:记录和重播。记录功能用于记录DOM中的所有动作变化; 重放是根据相应的时间戳逐个重放记录下的动作变化。
  3. rweb-player是 rrweb的一个玩家用户界面,可以随时提供基于GUI的功能,如暂停,快进,拖放等功能。

使用这些我们可用于web界面录制以及web界面重放这两个主要功能,rrweb-snapshot 返回的数据结构是json格式的,方便我们前后台数据对接,也规范了这款业务逻辑操作。

rrweb 实践

引入库

(1) 直接通过script引入,推荐通过 jsdelivr 的 CDN 安装:

<linkrel="stylesheet"href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>

我们在被录制的应用中只需要引入录制部分代码即可,无需全量引入。

<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/record/rrweb-record.min.js"></script>

(2) 通过 npm 引入rrweb 同时提供 commonJS 和 ES modules 两种格式的打包文件,易于和常见的打包工具配合使用。

npm install --save rrweb

兼容性

由于使用 MutationObserver API,rrweb 不支持 IE11 以下的浏览器。可以从mutationobserver兼容性列表中找到兼容的浏览器列表。

录制开始

如果通过 script 的方式仅引入录制部分,那么可以访问到全局变量 rrwebRecord,它和全量引入时的 rrweb.record 使用方式完全一致,具体看以下示例代码。

// 通过 <script> 的方式,全量引入、npm 引入的
start() {let self = thisthis.operation = rrweb.record({emit(event) {self.events.push(event);},});
}
// 通过 <script> 的方式,仅仅引入录制部分代码
start() {let self = thisthis.operation = rrwebRecord({emit(event) {self.events.push(event);},recordCanvas: true //支持录制canvas//其他参数});
}

rrweb 在录制时会不断将各类 event 传递给配置的 emit 方法,你可以使用任何方式存储这些 event 以便之后回放。
当前rrweb 只支持录制静态的canvas,无法录制动态js生成的canvas.

录制结束

录制结束,rrwebRecord再次执行一遍,即可停止录制。

stop() { this.operation && this.operation()
}

录制数据压缩

引入pako 来对录制的数据进行 gzip压缩,

npm install pako --save
zip(str) {let binaryString = pako.gzip(encodeURIComponent(str), { to: 'string' })return btoa(binaryString);
}

因为录制的是web页面,需要对数据进行encodeURIComponent 一遍,不然解压之后的数据会变成乱码

录制数据解压

unzip(b64Data) { var strData   = atob(b64Data);var charData  = strData.split('').map(function(x){return x.charCodeAt(0);});var binData   = new Uint8Array(charData);var data    = pako.inflate(binData);// strData   = String.fromCharCode.apply(null, new Uint16Array(data));let array = new Uint16Array(data)var res = '';var chunk = 8 * 1024;var i;for (i = 0; i < array.length / chunk; i++) {res += String.fromCharCode.apply(null, array.slice(i * chunk, (i + 1) * chunk)); }res += String.fromCharCode.apply(null, array.slice(i * chunk));strData = res   return decodeURIComponent(strData);
}

Uint16Array 对字符的长度有限制,所以采用分段解压的方式,这样就不会出现内存溢出的问题。

录制数据保存

save(events) {let self = thisif (self.events.length === 0 || events&&events.length === 0) {return Promise.resolve();}const data = JSON.stringify(self.events)let logsParams = { "message": data, "setMessageTime": (new Date().getTime() + '')}const body = JSON.stringify({"__topic__": "topic","__source__": "source","__logs__": [logsParams]});let url = 'xxx保存地址urlxxxxx'return new Promise((resolve, reject) => {fetch(url, {method: 'POST',headers: {'x-log-apiversion': '0.6.0','x-log-bodyrawsize': sizeof(body),'Content-Type': 'application/json',},body,}).catch(error => {reject(error)}).then(response => {resolve(response)})})
}

一个更接近实际真实使用场景的示例如下:

// 每 10 秒调用一次 save 方法,避免请求过多
setInterval(save, 10 * 1000);
// 每调用一次,清空数据
this.events = []

当前我们使用的阿里云的日志接口,也可以改成其他的类似的请求接口。类似官网差不多的。当然我们也可以根据各自的需求,改善自己提交数据的策略。

录制数据播放

(1) 如果 rrweb 是全局引入的情况下,回放时需要引入对应的 CSS 文件:

<linkrel="stylesheet"href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"
/>

再通过以下 JS 代码初始化 replayer:

const events = YOUR_EVENTS;
const replayer = new rrweb.Replayer(events);
replayer.play();

在实际的应用场景,我们需要异步请求数据,在对数据进行解压,然后再调用JS 。

fetch(url).catch(error => {reject(error)
}).then(res => {//const eventsDate = JSON.stringify(res.data)const events = unzip(eventsDate)const replayer = new rrweb.Replayer(events);replayer.play();
})

(2) 使用 rrweb-player
rrweb-player 同样可以使用 CDN 方式安装:

<linkrel="stylesheet"href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"
/>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>

或者通过 npm 安装:

npm install --save rrweb-player

使用方式:

new rrwebPlayer({target: document.body, // 可以自定义 DOM 元素data: {events,},UNSAFE_replayCanvas: true //支持回放 canvas
});

具体其他的api,或者其他的使用方式,大家可以查看官网

参考网址:

rrweb:打开 web 页面录制与回放的黑盒子
rrweb 官网
rrweb-snapshot 快照
rrweb-player 播放

基于rrweb框架对web 页面录制与回放相关推荐

  1. 实战Acegi:使用Acegi作为基于Spring框架的WEB应用的安全框架

    对于任何一个完整的应用系统,完善的认证和授权机制是必不可少的.在基于SpringFramework的WEB应用中, 我们可以使用Acegi作为安全架构的实现.本文将介绍如何在基于Spring构架的We ...

  2. 神策数据王磊:如何用 JS 实现页面录制与回放

    本文根据神策数据资深前端研发工程师王磊<如何用 JS 实现页面录制与回放>的直播整理而成.以下为正文: 一.业务背景 对于研发来说,总是需要处理一些线上问题.To B 和 To C 企业在 ...

  3. Ubuntu下利用ipmitool工具解决IPMI(基于openbmc)的web页面崩溃或出现session expired的问题

    1.问题出现 如上图,笔者在一年多前买到了一台DDN公司的存储服务器控制器,配置十分的豪华(3647CPU,支持第一代与第二代可拓展志强的正式版,qs与es,1U的空间,六通道内存,支持dcpmm,两 ...

  4. YMP框架学习笔记(二)------基于YMP框架搭建WEB应用程序

    2019独角兽企业重金招聘Python工程师标准>>> 大神的YMP框架:https://github.com/suninformation/ymateplatform.git 1. ...

  5. 基于Vue框架开发的页面加载二维地图以及交互

    一.在Vue项目中引入二维地图 1.切换到公司的仓库下载地图插件 npm config set registry http://nexus.toops.club/repository/npm-zui/ ...

  6. 基于Vue框架开发的页面加载三维维地图以及交互

    一.在Vue项目中引入三维地图 1.安装对应的包 //安装mars3d主库 npm install mars3d --save //安装mars3d插件(按需安装) npm install mars3 ...

  7. 基于SSM框架的杰森摄影工作室选片系统的设计和开发论文

    基于SSM框架的杰森摄影工作室选片系统的设计和开发 摘 要 面对巨大的市场和激烈的竞争,摄影/照相馆行业商家必须紧跟行业发展的步伐,导入新的经营理念和推广方式,才不至于在激烈的市场竞争中被淘汰出局.信 ...

  8. 实现基于Spring框架应用的权限控制系统(转)

    为什么80%的码农都做不了架构师?>>>    前注:当我摸到了通过filter拦截权限,通过AOP拦截方法,通过权限控制菜单的时候,猛然发现这一切的一切已经是别人已经发明好的轮子. ...

  9. PHP使用声网的页面录制、合流录制、单流录制

    为了写着三个录制,发了N多个工单,坑有点大 直接上正文 公共部分 页面录制 合流录制 单流录制 结束录制 结语 直接上正文 公共部分 /*** 获取声网ResourceId* @param $stud ...

  10. 基于python的界面自动化测试-基于Selenium+Python的web自动化测试框架

    一.什么是Selenium? Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台.跨浏览器的端到端的web自动化解决方案.Selenium主要包括三部分:Selenium IDE.S ...

最新文章

  1. DLL: 一种直接法的激光雷达定位方案
  2. centos7 php配置,CentOS7 下nginx与PHP的安装与配置
  3. http协议的Request Payload 和 Form Data 的区别
  4. 中国SaaS死或生之二:ERP两大邪术,尽出歪门邪路
  5. 各大视觉技术竞赛冠军及 TOP 方案集锦(持续更新)
  6. linux多线程计算pi,使用蒙特卡洛方法多线程计算pi值
  7. Spark action算子案例
  8. 可爱的PNG免扣新年装饰素材,让你海报增加节日气氛
  9. java怎么系统输入数字_java怎么输入数字,这些经验不可多得
  10. Win10+caffe+CUDA9.1+vs2013+Matlab2018b+GPU环境,跑通faster_rcnn-master
  11. Markdown中显示代码段的语法特殊字符`如何输入?
  12. SAP 客户端不能访问解决方案
  13. DP/eDP协议学习--视频传输格式
  14. 雷课堂(THUnderClass)——清华大学2020C++大作业个人项目记录与总结
  15. 国美易卡网络大数据,国美易卡MySQL数据库
  16. 韭菜简史:快招加盟的致富骗局
  17. git文件没有绿色红色小图标解决办法
  18. login 登陆界面
  19. NMAP手册中文版(附英文解释版)
  20. 2018第二届中国通信业物联网大会精彩前瞻

热门文章

  1. codeforces838D - Airplane Arrangements
  2. Cocos2d-x 3.x游戏开发之旅
  3. 既然选择了远方,便只顾风雨兼程
  4. C# WPF MVVM 实战 – 2.2
  5. 剑指offer之斐波那契数列求解
  6. 美团Robust热修复工具使用记录
  7. [ZT]毁人不倦的应试教育(2)
  8. nvm for Mac 安装及使用教程
  9. Leetcode 120. Triangle 三角形问题(动态规划经典) 解题报告
  10. CMD命令Program Files问题