埋点
埋点方式
所谓’埋点’是数据采集领域(尤其是用户行为数据采集领域)的术语,指的是针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。. 比如用户某个icon点击次数、观看某个视频的时长等等。
埋点实际上是对特定事件或者行为的数据监控和上报,常见的埋点上报方式有ajax,img,navigator.sendBeacon。
基于ajax的埋点上报
介绍
因为埋点实际上是对关键节点的数据进行上报是和服务端交互的一个过程,所以我们可以和后端约定一个接口通过ajax去进行数据上报。
代码实现
我们可以封装一个方法,代码如下:
1 | function buryingPointAjax(data) { |
使用时,直接调用即可
1 | let info = {} |
缺点
一般而言,埋点域名并不是当前域名,因此请求会存在跨域风险,且如果ajax配置不正确可能会浏览器拦截。因此使用ajax这类请求并不是万全之策。
基于img的埋点上报
上面可以看到如果使用ajax的话,会存在跨域的问题。而且数据上报前端主要是负责将数据传递到后端,并不过分强调前后端交互。
因此我们可以通过一些支持跨域的标签去实现数据上报功能。
1 | script,link,img就是我们上报的数据的最好对象 |
先说结论,这里推荐使用img标签去实现。
script及link的缺陷
因为埋点涉及到请求,因此我们需要保证script和link标签的src可以正常请求。如果使用script和link,我们需要将标签挂载到页面上。
验证缺陷
不妨验证下,我们在管理台中加入以下代码:
1 | let a = document.createElement('script') |
创建一个script标签,未挂载中页面上,并不会发起请求
书接上文,当我们将这个标签挂载中页面上时:
1 | document.body.appendChild(a) |
这时发起了请求
结论
当我们使用script和link进行埋点上报时,需要挂载到页面上,而反复操作dom会造成页面性能受影响,而且载入js/css资源还会阻塞页面渲染,影响用户体验,因此对于需要频繁上报的埋点而言,script和link并不合适。
基于img做埋点上报
通常使用img标签去做埋点上报,img标签加载并不需要挂载到页面上,基于js去new image(),设置其src之后就可以直接请求图片。
验证img优势
控制台去创建一个image标签,如下:
1 | var img=new Image(); |
可以看到即便未被挂载到页面上依旧发起了请求。
结论
因此当我们做埋点上报时,使用img是一个不错的选择。
- img兼容性好
- 无需挂载到页面上,反复操作dom
- img的加载不会阻塞html的解析,但img加载后并不渲染,它需要等待Render Tree生成完后才和Render Tree一起渲染出来
注:通常埋点上报会使用gif图,合法的 GIF 只需要 43 个字节
基于Navigator.sendBeacon的埋点上报
Navigator.sendBeacon是目前通用的埋点上报方案,Navigator.sendBeacon方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等等)。
介绍
navigator.sendBeacon()
方法可用于通过 HTTP POST 将少量数据 异步 传输到 Web 服务器。
作用
它主要用于将统计数据发送到 Web 服务器,同时避免了用传统技术(如:XMLHttpRequest
)发送分析数据的一些问题。
补充
sendBeacon 如果成功进入浏览器的发送队列后,会返回true;如果受到队列总数、数据大小的限制后,会返回false。返回ture后,只是表示进入了发送队列,浏览器会尽力保证发送成功,但是否成功了,不会再有任何返回值。
例子
以掘金为例:
这里发了一个post请求,将小量的数据发到服务端,用于统计数据
优势
相较于img标签,使用navigator.sendBeacon会更规范,数据传输上可传输资源类型会更多。对于ajax在页面卸载时上报,ajax有可能没上报完,页面就卸载了导致请求中断,因此ajax处理这种情况时必须作为同步操作.
sendBeacon是异步的,不会影响当前页到下一个页面的跳转速度,且不受同域限制。这个方法还是异步发出请求,但是请求与当前页面脱离关联,作为浏览器的任务,因此可以保证会把数据发出去,不拖延卸载流程。
总结
- 前端埋点上报常使用ajax,img,navigator.sendBeacon。
- 不推荐使用ajax
- 如果考虑兼容性的话,img是不二之选。
- 目前最合适的方案是navigator.sendBeacon,不仅是异步的,而且不受同域限制,而且作为浏览器的任务,因此可以保证会把数据发出去,不影响页面卸载。
常见埋点行为
点击触发埋点
绑定点击事件,当点击目标元素时,触发埋点上报。
1 | function clickButton(url, data) { |
页面停留时间上报埋点
路由文件中,初始化一个startTime,当页面离开时通过路由守卫计算停留时间。
1 | let url = ''// 上报地址 |
错误监听埋点
通过监听函数去接收错误信息。
vue错误捕获
1 | app.config.errorHandler = (err) => { |
JS异常与静态资源加载异常
1 | window.addEventListener('error', (error) => { |
请求错误捕获
1 | axios.interceptors.response.use( |
内容可见埋点
通过交叉观察器去监听当前元素是否出现在页面
1 | // 可见性发生变化后的回调 |
后续
开发的时候可以封装这三种上报方法
1 | // sendBeacon 上报 |
使用的时候再导出一个真实上报函数,有由用户决定使用什么上报组合
1 | // 基础上报函数 |
Navigator.sendBeacon()相比XMLHttpRequest()的好处
用户卸载网页的时候,有时需要向服务器发一些数据。很自然的做法是在unload
事件或beforeunload
事件的监听函数里面,使用XMLHttpRequest
对象发送数据。但是,这样做不是很可靠,因为XMLHttpRequest
对象是异步发送,很可能在它即将发送的时候,页面已经卸载了,从而导致发送取消或者发送失败。
解决方法就是unload
事件里面,加一些很耗时的同步操作。这样就能留出足够的时间,保证异步 AJAX 能够发送成功。
1 | function log() { |
上面代码中,强制执行了一次双重循环,拖长了unload
事件的执行时间,使得异步 AJAX 能够发送成功。
上面这种做法卸载时间被硬生生拖长了,后面页面的加载被推迟了,用户体验不好。
为了解决这个问题,浏览器引入了Navigator.sendBeacon()
方法。这个方法还是异步发出请求,但是请求与当前页面线程脱钩,作为浏览器进程的任务,因此可以保证会把数据发出去,不拖延卸载流程。
1 | window.addEventListener('unload', logData, false); |