众所周知, hotjar 中录屏功能是其重要的一个卖点, 看着很牛 X 酷炫的样子, 今天就简单的分析一下其可能实现 (这里只根据其请求加上个人理解分析, 并不代表 hotjar 中真实实现必然如此) 的原理.
1, 获取完整 DOM 内容
如果要实现完整的录屏功能, 在客户端在没有客户允许的前提下,目前是无法做到的, 所以只能考虑在服务端来实现, 在服务端实现的第一步, 就必然需要重现客户端的渲染结果, 此时需要完整的发送客户端内容到服务端, 在服务端进行完整的渲染.
从布玛的效果来看, 获取 DOM 内容会涉及如下三个请求:
请求 1 用来判断该页面内容在服务端是否存在
- Request URL: https://in.hotjar.com/API/v1/sites/848493/pages/2338866123/content-id/34b2f50d09fbe08a4444e6691b1be779
- Request Method: GET
- Status Code: 200
- // 响应结果
- {
- "exists": false
- }
请求 2 预检请求 没啥说的, 应该都知道干啥的
- Request URL: https://in.hotjar.com/API/v1/sites/848493/url-hash/4f9e7b2f60ba35900e873ed12b1502ec/content
- Request Method: OPTIONS
- Status Code: 200
请求 3 发送完整 DOM 内容
- Request URL: https://in.hotjar.com/API/v1/sites/848493/url-hash/4f9e7b2f60ba35900e873ed12b1502ec/content
- Request Method: POST
- Status Code: 200
- Request Payload
- content: "{"docType":"<!DOCTYPE html>\n","rootId":1,"childre" .......
- content_md5: "34b2f50d09fbe08a4444e6691b1be779"
- page_id: 2338866123
- page_url: "xxxxxxxxx.HTML"
- // 响应结果
- {"page_content_id": 8784354270, "success": true}
从请求 3 中可以看到, content 部分其实就是对完整 HTML 的 JSON 化, 这部分内容比较长, 只贴出部分内容.
2, 获取鼠标移动轨迹
只是获取完整 DOM 内容只是第一步, 在 hotjar 的录屏功能中, 还有一个是获取鼠标运动轨迹, 想要绘制运动轨迹, 必然要知悉鼠标在时间轴上的位置信息, 所以 hotjar 中, 必然要采集鼠标在不同时间点的位置信息, 这个可以通过其 websocket 请求
- Request URL: wss://ws7.hotjar.com/API/v1/client/ws
- Request Method: GET
- Status Code: 101 Switching Protocols
在 ws 请求过程中, 会有 mouse-move 数据包的发送, 其基本结构如下:
- mouse_move: [{
- time: 106597, x: 215, y: 115
- }, {
- time: 106695, x: 181, y: 105
- }, {
- time: 106796, x: 134, y: 139
- },...]
- page_visit_id: 14777325238
- page_visit_key: "e9fa998e-5811-4d2f-81d2-bd296c7129af"
其中可以看到 mouse_move 数据结构中, 包含了时间轴上不断变化的坐标值(x,y), 有了基于时间轴的 xy 坐标, 我们绘制内容就变的不那么复杂了.
3, 检测并发送 DOM 变化
除了鼠标运行轨迹之外, 用户在页面上的所有行为都会被完整的记录下来, 页面的任何变化也都被记录了下来, 如果需要在服务端完整的重新演化这种变化, 那么需要把完整的变化结构发送到服务器, 让服务端进行变化回溯, hotjar 是通过 ws 中发送 mutation 发送这种结构包的, 当然要发送这种结构包, 首先要先观测 DOM 变化, 这里也有一种简单的方式(暂时不确定 hotjar 的实现)HTML5 DOM4 级 MutatioObserver 方法, 可以检查页面中的 DOM 是否发生变化, 大家可以做一下简单的测试:
- // 选择目标观测节点
- let target = document.querySelector('目标节点选择器');
- // 创建观察者对象
- var observer = new Windows.MutationObserver(function(mutations) {
- mutations.forEach(function(mutation) {
- console.log(mutation);
- });
- });
- // 观测项配置:
- var config = { attributes: true, childList: true }
- // 开始观测目标节点
- observer.observe(target, config);
获取到变化的 DOM 结构(这种变化也是时序的变化, 因为任何操作都可能导致变化, 变化必然是有先后顺序的), 然后通过 ws 发送到服务器, 通过 Chrome network 可以看到 ws 中 mutation 基本的包结构如下:
- mouse_move: [{
- time: 118994, x: 404, y: 135
- }]
- mutation: [{
- time: 118308,...
- }, {
- time: 118312,...
- }, {
- time: 118336, c: [{
- id: 7480,...
- }]
- }
- page_visit_id: 14777325238
- page_visit_key: "e9fa998e-5811-4d2f-81d2-bd296c7129af"
里面包含了 DOM 节点变化, 其中包含变化节点如何变化的(通过节点的所有 attribute 来应用)
4, 变化和轨迹回溯生成视频
最后一切数据准备完毕, 需要生成视频了, 生成视频当然涉及很多的计算, 因为要演化和回溯用户的所有操作, 我猜可能的思路是这样的:
在服务器启动浏览器 并 启动录屏软件(录屏软件只是猜测可能有其它多种方式)
根据页面发送的完整 DOM 进行初始化内容展示
按照时序合并鼠标轨迹和 mutation 包数据
根据时间轴自动操作改变 DOM
访次结束完成录制
总结
hotjar 中还涉及到更多的细节实现, 里面很多内容也并没有考虑, 比如发送 view_port report_content 包等都没什么在文章中体现出来, 但这些并不影响主线分析, 另外因为只是简要分析, 所以并不涉及实现细节, 有兴趣的欢迎留言讨论.
来源: https://www.cnblogs.com/Johnzhang/p/9767304.html