前端埋点主要是为了服务运营人员采集用户行为数据,进行后续的数据分析工作。
前端监控和埋点能做什么
- 数据监控(用户行为)
- pv,uv
- 记录操作系统
- 用户在每一个页面的停留时间(离开页面,进入页面)
- 用户进入的入口
- 用户在相应页面的触发行为,点击按钮
- 性能监控 (js中的performance)
- 用户的首屏加载
- http请求响应时间
- 页面渲染时间
- 页面交互动画完成时间
关键代码
let timing = performance.timing,
start = timing.navigationStart,
dnsTime = 0,
tcpTime = 0,
firstPaintTime = 0,
domRenderTime = 0,
loadTime = 0;
//DNS解析时间
dnsTime = timing.domainLookupEnd - timing.domainLookupStart;
//TCP建立时间
tcpTime = timing.connectEnd - timing.connectStart;
//首屏时间
firstPaintTime = timing.responseStart - start;
//dom渲染完成时间
domRenderTime = timing.domContentLoadedEventEnd - start;
//页面onload时间
loadTime = timing.loadEventEnd - start;
| 域名( domain ) | javascript | document.domain ;获取的值如:"domain" : "127.0.0.1" |
| URL (url) | javascript | document.URL;获取的值如:"url" : "http://127.0.0.1:3000/" |
| 页面标题 (title) | javascript | document.title;获取的值如:"title" : "Express"; |
| 上一跳url、referrer (referrer) | javascript | document.referrer;获取的值如:"referrer" : "" ; |
| 分辨率 (height:sh; width: sw) | javascript | window.screen.height & width; 获取的值如:"sh" : "1050" ,"sw" : "1680"; |
| 颜色深度 (cd) | javascript | window.screen.colorDepth; 获取的值如:"cd" : "32"; |
| 客户端语言 (lang) | javascript | navigator.language;获取的值如:"lang" : "zh-CN"; |
| user-agent header(userAgent) | javascript | navigator.userAgent;获取的值如:"userAgent" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36"; |
现有的前端埋点方案总结
-
手动埋点
1.命令埋点,前端代码中需要监控的地方插入监控逻辑// 页面加载时发送埋点请求 $(document).ready(function(){ // ... 这里存在一些业务逻辑 sendRequest(params); }); // 按钮点击时发送埋点请求 $('button').click(function(){ // 这里存在一些业务逻辑 sendRequest(params); });
2.声明式埋点
声明式埋点的思路是将埋点代码和具体的交互和业务逻辑解耦,开发者只用关心需要埋点的控件,并且为这些控件声明需要的埋点数据即可,从而降低埋点的成本 ,在dom元素上增添埋点信息,如下
// key表示埋点的唯一标识;act表示埋点方式 <button data-stat="{key:'111', act: 'click'}">埋点</button>
相比命令式埋点,不至于傻瓜式的在哪监控在哪埋点
遍历dom树,找到[data-stat]元素的节点,绑定click事件,将[data-stat]上的信息发送给服务器
缺点:
1.遍历DOM树的时机问题,一个简单的例子,一个表格的行数据是通过异步加载,而表格行中的操作按钮需要埋点,那么在DOM ready的时候去遍历,显然是无法找到的
2.绑定埋点事件次数的问题,怎样保证埋点事件不会被重复绑定到元素上,一次操作发了N个埋点请求
重复工作很多,还要处理冒泡。可视化埋点
业内开源解决方案:Mixpanel
与配套的可视化页面搭建和
运营通过可视化的界面
拖拽配置实现,这些活动控件元素都带有唯一标识。通过埋点配置后台,将元素与要采集事件关联起来,可以自动生成埋点代码嵌入到页面中。-
全埋点
无埋点则是前端自动采集全部事件,上报埋点数据,由后端来过滤和计算出有用的数据,优点是前端只要一次加载埋点脚本。缺点是流量和采集的数据过于庞大,服务器性能压力山大,主流的 GrowingIO 就是这种实现方案。
SDK就会自动追踪页面上的按钮(a
、button
、input
) 这种html标签类型的点击情况,一旦页面某一个按钮发生了点击行为,SDK就会去采集此按钮的一些信息,例如: 这个按钮的标签类型,这个按钮的文本内容,这个按钮的name
,这个按钮的id
、class
名,还有一些按钮特有的属性如href
等。
比如click事件,在document上绑定click,在事件中的捕获阶段进行绑定,即使按钮元素取消冒泡了,也跟不会影响捕获阶段的传递(在页面中点击一个元素,事件是从这个元素的祖先元素中逐层传递下来的,这个阶段为事件的捕获阶段。当事件传递到这个元素之后,又会把事件逐成传递回去,直到根元素为止,这个阶段是事件的冒泡阶段 )
事件标识?怎么唯一定位到某个页面的元素,设定一个根节点,根节点到这个元素自顶向下的属性名
缺点:dom结构可能会变,class 名字, 元素嵌套,很难唯一定位到
-
美团实现方案
70%全埋点 + 30%手动埋点
在不同场景下我们需要选择不同的埋点方案。例如对于简单的用户行为类事件,可以使用全埋点解决;而对于需要携带大量运行时才可获知的业务字段的埋点需求,就需要声明式埋点来解决。从更高的层面来看
思考
前端路由
前端路由通过‘#’锚点,其本来加在URL中指示网页的位置的,hash虽然出现在URL中,但不会被包括在HTTP请求中。它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面。
改写history.replaceState
数据上传方式
- img标签上传
- ajax
- 带来跨域问题