实现埋点上报方案过程当中的思考

任何一个重视数据的公司与部门,都不会忽略前端埋点数据上报的做用。javascript

从直观上看,前端的埋点上报方案有啥难的呀?不就是将用户行为数据收集上来,就能够了么?前端

从实际开发经验来回答:实际上并无那么简单。为了可以将收集的用户数据最终展现并从中分析得出有意义的数据,这其中涉及的细节比想象的要多。java

在本文中,咱们会从两个方面来思考数据埋点上报的过程:数据收集与数据上报。ios

数据收集

对于数据收集的过程,咱们不会对数据上报方式作区分(如常见的埋点上报与无埋点上报),而重点思考无论哪一种上报方式都通用的技术点。重点考察以下基本的数据信息:UV、PV、应用浏览总时长。web

UV

针对UV的统计维度有两个:注册用户与未注册用户。小程序

注册用户好说,只须要有应用中注册用户的惟一标识符便可。跨域

对于未注册用户,如何识别这是一个用户呢?在这种状况下,问题会转化为:如何识别一个设备?浏览器

如何识别一个设备?

最容易想到的就是利用浏览器的存储功能:cookie、localStorage等。具体作法是:在用户首次加载页面的时候,将UUID(如随机生成的字符串)放入到存储中,这种方案是最简单的,其中:bash

Cookie 有生命时长限制而且有容量大小的限制。服务器

localStorage 会永久保存,而且在容量方面比cookie大得多。

因此,使用localStorage就会比cookie更有效。可是,基于浏览器存储功能的方案,有一个天生的缺陷:用户能够选择主动删除存储

这个时候,开发者就会想:是否可以有一个真正的与设备相关的惟一标识符呢?

因而有了浏览器指纹追踪技术:相似人的外貌和指纹,Web客户端(这里主要指浏览器)也有多种“外貌”信息和“指纹”信息,将这些信息综合分析计算后,可对客户端进行惟一性识别,进而锁定、追踪、了解网民行为和隐私数据。

在 WA 中,引用了公司内部的另外一个基于浏览器指纹追踪库做为识别同一个浏览器访问的惟一标识符。通过咨询该库开发者 ,发现浏览器指纹技术在实际应用中的效果并很差(没法识别的几率很高):

前提一:   只针对浏览器,换了浏览器即便同设备,id也不同。
前提二:   用ip区分用户,不一样ip认为是不同的用户。

统计结果: 18年咱们在生产环境作了测试,ios 7800个用户中,有22%的用户id未发生重复id。安卓15000个用户中,有39%未发生重复id。

缘由分析: 由于浏览器拿不到设备底层数据,只能经过一些浏览器的配置参数去生成id。若是用户手机系统 型号 浏览器版本和相关配置同样,那生成的就同样。
复制代码
结论

通过综合考虑,咱们放弃了浏览器指纹追中技术。在咱们设计的埋点方案中,非注册用户的UV统计方式采用的是植入UUID到localStorage的方案。

PV

当页面发生改变时,咱们就会在PV计数上增长1。对于此,咱们想问:如何知道页面变化了?

这个问题,能够细分为两个小问题:

  1. 浏览器本身是否能够彻底知道页面发生了变化?
  2. 开发者本身是否能够彻底知道页面发生了变化?

第二个问题,答案是确定的。开发者固然了解页面啥时候发生了变化。可是第一个问题,考虑浏览器是否能够知道页面发生了变化的时候,有趣的点就来了:

浏览器认为的页面变化是否就是咱们所须要的页面变化呢?

对于多页面应用,经过侦听 window.onLoad 事件与 window.onunload 事件,或者侦听URL的变化 onhashchange 便可。浏览器认为的页面变化就是咱们所须要的页面变化。

对于单页面应用呢?这可就不必定了。

由于是单页面应用,那么只会有一次页面加载事件的触发。也就是说,没法经过侦听 window.onLoad 事件与 window.onunload 事件来实现。

那么,经过侦听URL的变化可行么?若应用中存在多层嵌套路由,当URL变化了,不必定表明着页面发生了变化。

这个时候怎么办呢?咱们来看看大厂们的作法:

Google Analytics:

ga网页浏览说明

GrowingIO:

企业微信截图_13b09295-1318-4cc0-91c6-1b53ea0e289d

易观方舟:

企业微信截图_52c8297c-f8e4-457c-9933-95ab1e806215

针对单页面应用时的PV统计,基本都提供了这样的方式:

1.配置信息:是否启动基于URL变化的自动页面收集方式。

2.手动上报:提供接口主动上报页面变化。

因此,在咱们的实现中,也基于这样的方案来实现。

应用浏览总时长

在应用浏览时长的统计阶段,关键的细节在于如何准确了解应用被关闭了。咱们来看正常状况和非正常状况。

正常:在正常应用关闭的操做中,咱们能够侦听 window.onunload 事件便可。

非正常:那么非正常状况呢?好比这些:

  • 浏览器闪退

  • 用户很长时间未操做的状况下(好比看视频),浏览器忽然被闪退

  • H5 页面/小程序的客户端闪退/被杀死

这些异常状况下,该如何处理呢?

一种推荐的方案是:WebSocket。前端SDK与后台创建一个webSocket持久性链接。webSocket链接中的心跳检测是必备选项,用于应对网络不稳定。当应用被关闭的时候,服务器端能够知道这个关闭事件。缺点:WebSocket方案有必定的兼容性问题。

(ps: webSocket的更多细节原理并未亲自实现)

在咱们的应用中,是基于 WA 上报数据。它并无这样一个webSocket的持久性链接提供出来。该怎么办呢?

现阶段是这么作的:

思考:在客户端被杀死或者崩溃退出的状况下,web 页面自己并无合适的事件可以精确监听。

那么,采用近似的思路来趋近:在现有的方案中经过设置一个合适时长的按期上报事件(定时心跳上报),经过获取本次会话的最后上报时刻,可近似理解为是用户关闭页面(最大偏差为所设置的心跳上报间隔)。

对于这种方案的实现,须要数据统计端的配合:在计算应用总时长的时候,需考虑心跳上报因素。

数据上报

对于一个埋点方案来讲,数据上报有两个点须要着重考虑:

  • 对跨域作特殊处理。
  • 页面销毁后,如何还可以将未上传的埋点数据成功上报?

传统的XHR发送数据请求的方式,对上面提到的两个点都无能为力。在数据上报过程当中,较为经常使用的有两种方式:

  • 动态建立img标签,在 img.src 中拼接url的方式发送请求
  • 调用 sendBeacon 接口发送数据

Major tracking upgrade - sendBeacon(), etc! - Clicky Blog

Img 标签

利用img标签的src属性发送请求的方式,具体执行方案以下:

let _img = new Image();
_img.src = `${url}?${util.spliceParam(params)}`;
_img.onload = _img.onerror = function() {}
复制代码

它很是契合埋点数据上报这个应用场景:

① 只上报的数据不须要接收响应;

② img的src属性自然地不存在跨域问题。

这种使用方式也存在缺陷。首先对于src 中的URL内容是有大小限制的,太大的数据量不适用。详细看这里。其次,在页面卸载的时候,若存在数据未发送的状况,会先将对应的数据发送完,再执行页面卸载。这种状况下,会在体验上给使用者带来不方便。

此时的 sendBeacon方法就是来解决上面提到的缺陷的。

sendBeacon

sendBeacon方法是一个异步、非阻塞的数据传输方法。具体使用方式以下:

window.navigator.sendBeacon && window.navigator.sendBeacon(url, params)
复制代码

它的特色是:

  • Beacon请求是Post方式。
  • Beacon请求优先避免与关键操做和更高优先级的网络请求竞争。
  • Beacon请求能够有效地合并,以优化移动设备上的能量使用。
  • Beacon保证页面卸载以前启动信标请求,并容许运行完成且不会阻塞请求或阻塞处理用户交互事件的任务。
  • 返回值:sendBeacon 方法被执行后返回一个布尔值,true表明用户代理成功地将信标请求加入到队列中,不然返回false

对于sendBeacon方法,它的局限性体如今:

  • 不能跨域,须要服务端设置。
  • 新特性接口,兼容性存在问题。

所以,在实际的应用过程当中,须要针对实际状况,结合 Img 标签 与 sendBeacon 的方式一块儿使用。

总结

在本文中,咱们分别讨论了在埋点上报阶段的两个方面:数据收集与数据上报。这其中,对于数据收集过程当中的一些值得留意的状况,咱们作了分析。同时,与你分享了两种经常使用的数据上报方式。但愿对你有用!

相关文章
相关标签/搜索