摘要: 网页应该如何录屏呢?
Fundebug 经授权转载, 版权归原作者所有.
关键点
首先, 每一次会话都有一个唯一的 session ID, 这是串联起所有行为的纽带.
其次, 用户行为又分成两个部分, 其一是用户的操作, 比如鼠标滑动, 点击, 页面滚动等, 其二是页面的变化. 这两者我们都统称为用户行为, 记录在同一个队列中.
一开始的时候, 系统会记录下初始的页面作为第一帧, 这是唯一的一次完整页面记录.
针对用户操作, 我们会记录事件的类型, 鼠标位置等关键信息, 保存到队列中.
针对页面变动, 我们会起一个 mutationObserve 侦听页面的改动, 每次只记录改动的部分, 保存到队列中.
无论是事件还是页面改动, 都是对等的一帧, 每一帧都会有当前时间, 与上一帧间隔时间等基本信息用户还原
一旦出错, SDK 就把队列发送到监控系统, 并清空当前队列.
还原端根据记录的行为队列, 根据时间逐一播放出来. 最终形成一个类似于视频的效果.
初步思路
方式一:
前端收集信息, 首先, 初始化的时候记录一个页面的初始状态, 然后利用 MutationObserver 监听 dom 的改变事件, 然后监听所有的鼠标事件, 滚动事件等等所有的页面变化.
在合理的时机把这些信息队列上传到服务器, 如页面出错时等.
后台分析前端收集到的信息, 转为图片, 然后形成 "视频", 或者用户行为栈. 提供对应的调用 API.
前端需要查找问题时, 根据用户 id 等信息找到对应的出错栈.
方式二:
前端根据 html 转为对应的图片 (可以转为 base64 格式)
将图片发送给后台
后台将图片按序组成 "视频"
现有 SDK
Fundebug
录屏 (截图)
- html2canvas
- puppeteer
- rrweb+rrweb-player+rrweb-snapshot
html2canvas 介绍
html2canvas 是通过分析页面中已加载好的 DOM 元素, 然后 canvas 将生成的 DOM 节点绘制在画布上, 最后转换为图片. 它不是真正的截屏, 只是根据页面元素信息还原出图片, 所以并不是 100% 和页面相同的.
局限性
页面中的图片不能跨域
不是所有的 CSS 特性都支持, 如不支持 box-shadow,filter 等
不支持截取插件内容, 如 Flash
不支持 iframe 内容
浏览器支持
- Firefox 3.5+
- Google Chrome
- Opera 12+
- IE9+
- Edge
- Safari 6+
puppeteer 介绍
Puppeteer 是 Google Chrome 团队官方的无界面 (Headless)Chrome 工具, 它是一个 Node 库, 提供了一个高级的 API 来控制 DevTools 协议上的无头版 Chrome .
局限性
Puppeteer 需要 Chromium. 其主要应用在自动化测试上.
功能
生成页面的截图和 PDF.
抓取 SPA 并生成预先呈现的内容 (即 "SSR").
从网站抓取你需要的内容.
自动表单提交, UI 测试, 键盘输入等
创建一个最新的自动化测试环境. 使用最新的 JavaScript 和浏览器功能, 直接在最新版本的 Chrome 中运行测试.
捕获您的网站的时间线跟踪, 以帮助诊断性能问题.
结论 html2canvas 更适合于 C 端的用户行为截图跟踪, 而 Puppeteer 适用于自动化测试.
rrweb 介绍
rrweb 主要由 3 部分组成:
rrweb-snapshot, 包含 snapshot 和 rebuild 两个功能. snapshot 用于将 DOM 及其状态转化为可序列化的数据结构并添加唯一标识; rebuild 则是将 snapshot 记录的数据结构重建为对应的 DOM.
rrweb, 包含 record 和 replay 两个功能. record 用于记录 DOM 中的所有变更 (mutation);replay 则是将记录的变更按照对应的时间一一重放.
rrweb-player, 为 rrweb 提供一套 UI 控件, 提供基于 GUI 的暂停, 快进, 拖拽至任意时间点播放等功能.
rrweb 适用场景:
用户行为分析;
远程 debug;
录制操作;
实时协作;
局限性
社区资源较少
部分代码用较旧的模式写的, 有未知坑
最终结论
综合来看, 结合思路一, 基于 rrweb 来开发是最可行最快捷的.
Demo
目前, 我基于 rrweb 已经做了个 demo 出来. 以下是初步成果: demo 代码
补充资料
rrweb 的一些思路原理
rrweb: 打开 Web 页面录制与回放的黑盒子
MutationObserver 介绍
Mutation Observer API 用来监视 DOM 变动. DOM 的任何变动, 比如节点的增减, 属性的变动, 文本内容的变动, 这个 API 都可以得到通知.
特点
它等待所有脚本任务完成后, 才会运行 (即异步触发方式).
它把 DOM 变动记录封装成一个数组进行处理, 而不是一条条个别处理 DOM 变动.
它既可以观察 DOM 的所有类型变动, 也可以指定只观察某一类变动.
- example
- Select the node that will be observed for mutations var targetNode = document.getElementById('some-id');
- // Options for the observer (which mutations to observe)
- var config = { attributes: true, childList: true, subtree: true };
- // Callback function to execute when mutations are observed
- var callback = function(mutationsList, observer) {
- for (var mutation of mutationsList) {
- if (mutation.type == "childList") {
- console.log("A child node has been added or removed.");
- } else if (mutation.type == "attributes") {
- console.log(
- "The" + mutation.attributeName + "attribute was modified."
- );
- }
- }
- };
- // Create an observer instance linked to the callback function
- var observer = new MutationObserver(callback);
- // Start observing the target node for configured mutations
- observer.observe(targetNode, config);
- // Later, you can stop observing
- observer.disconnect();
observe 方法接受两个参数, 第一个是所要观察的 DOM 元素是 article, 第二个是所要观察的变动类型 (子节点变动和属性变动), 方法调用时必须指定一种或多种变动类型, 否则报错, 变动类型如下:
- boolean childList = false;
- boolean attributes;
- boolean characterData;
- boolean subtree = false; // 表示是否将该观察器应用于该节点的所有后代节点.
- boolean attributeOldValue; // 表示观察 attributes 变动时, 是否需要记录变动前的属性值.
- boolean characterDataOldValue; // 表示观察 characterData 变动时, 是否需要记录变动前的值.
- sequence<DOMString> attributeFilter;// 数组, 表示需要观察的特定属性 (比如 ['class','src'])
disconnect 方法用来停止观察. 调用该方法后, DOM 再发生变动, 也不会触发观察器. takeRecords 方法用来清除变动记录, 即不再处理未处理的变动. 该方法返回变动记录的数组.
MutationRecord 对象
DOM 每次发生变化, 就会生成一条变动记录 (MutationRecord 实例). 该实例包含了与变动相关的所有信息. Mutation Observer 处理的就是一个个 MutationRecord 实例所组成的数组. MutationRecord 对象包含了 DOM 的相关信息, 有如下属性:
type: 观察的变动类型 (attribute,characterData 或者 childList).
target: 发生变动的 DOM 节点.
addedNodes: 新增的 DOM 节点.
removedNodes: 删除的 DOM 节点.
previousSibling: 前一个同级节点, 如果没有则返回 null.
nextSibling: 下一个同级节点, 如果没有则返回 null.
attributeName: 发生变动的属性. 如果设置了 attributeFilter, 则只返回预先指定的属性.
oldValue: 变动前的值. 这个属性只对 attribute 和 characterData 变动有效, 如果发生 childList 变动, 则返回 null.
关于 Fundebug
Fundebug 专注于 JavaScript, 微信小程序, 微信小游戏, 支付宝小程序, React Native,Node.JS 和 Java 线上应用实时 BUG 监控. 自从 2016 年双十一正式上线, Fundebug 累计处理了 20 亿 + 错误事件, 付费客户有阳光保险, 核桃编程, 荔枝 FM, 掌门 1 对 1, 微脉, 青团社等众多品牌企业. 欢迎大家免费试用!
来源: http://www.jianshu.com/p/db15fb390097