一、确定方案
最近在做一个数据统计的需求,查找了很多第三方数据追踪、统计工具,都没有完美适配需求的。最终在某次头脑风暴会上敲定了原生实现的方案。
首先要明确的就是收集数据的工作,也就是埋点,什么时候采集数据,采集什么数据,这些都是需要明确的,正确的采集到数据以后再向后端发送,后端进行存储和处理,处理完以后以图表、文字等各种分析结果的形式呈现,这也是使这些采集到的数据真正产生意义的地方。
需求中需要统计某些特定页面的访问数据,进入该页面的入口,该页面下产品的加购、下单、支付数据,以此计算出加购率、下单率、支付率,分析转化率,形成图表等进行展示。实际这就是统计一整条完整的客户消费链路,从哪个入口被吸引进哪个页面,对哪个产品产生兴趣进行加购以及后续下单支付。
在实现的过程中主要的难点就是如何正确记录这个操作链路以备后续提交数据,用户的操作场景是复杂的,站内各种跳转、地址栏输入url访问、从站外链接点进来、浏览器前进后退、页面刷新、开了多个浏览器窗口等等……
最终确定了一个方案,我称之为“入口生成id”算法,核心就是只要进入新入口就生成新的操作链路id。先说提交浏览数据,只要进入了特定页面就提交浏览数据,特定页面的每次访问都一定对应着某入口(这里把手输url、站外链接都看成是入口);再说提交加购数据,假如从该页面进入某产品页面完成加购是我们期待的行为或者说想统计的操作链路,但是用户可能没有按照预期的路径操作,可能是地址栏手动输入某url离开该页面,可能是点击了非预期的某模块进入了非预期的页面,这种情况下之前的操作链路就相当于结束或者断开了,需要生成新的操作链路id,因为它已经偏离了我们想统计的方向。这时我们就可以把非预期的模块都看成新入口,如统一成“TBD"(随便取的),只要用户点击了这些模块就相当于刷新了入口进入了新页面,生成了新的操作链路id,在完成加购的时候进行入口判断,如果是TBD入口说明是以非预期的方式完成加购的,则不提交数据计入统计;最后是下单和支付数据,用户加购以后何时下单支付是无法预计的,因此无法直接将下单和支付同当前记录的操作链路id和页面数据联系起来(也许现在从购物车下单的产品是三个月以前完成加购的,而此时本地存储记录的是当下的操作链路),所以需要后端在加购的时候将原来的加购产品与操作链路联系起来,从购物车下单支付的时候可以由该产品找到对应的操作链路。
操作序列的id要求具有唯一性,因此使用了uuid,直接npm引入项目就可以:
// 首先安装uuid
npm install uuid
// 在代码中使用它
const { v4: uuidv4 } = require('uuid');
const uuid = uuidv4();
console.log(uuid);
// 这将生成一个随机的UUID,类似于f47ac10b-58cc-4372-a567-0e02b2c3d479
记录操作链路最终使用了indexedDB + localStorage + sessionStorage的方案,这三种存储方式都有各自的优缺点,如果只取其一用是不够满足存储要求的,结合使用才是最合适的。
- indexedDB用于存储历史,考虑到浏览器前进后退的功能,使用indexedDB存储历史,方便浏览器前进后退时回溯之前对应的入口、页面类型等数据,发挥了indexedDB大容量存储的特点;
- localStorage用于存储lastTab(网站某些点击行为会弹出新的浏览器窗口,此时需要延续原来窗口下的操作id,页面数据)、lastTabIndex(新开浏览器窗口时取索引用,使每一个窗口对应不同的索引)、tabNum(记录浏览器窗口数量),发挥了localStorage在同一域名下的不同浏览器窗口共享数据的特点;
- sessionStorage用于存储当前浏览器窗口对应的索引,当前窗口下对应的入口信息,发挥了sessionStorage的会话级