微信小程序使用原生,不使用任何框架,不使用任何sdk,自主开发上报,手动埋点,曝光,数据集成等。
背景
用户行为日志和埋点是所有电商绕不过去的功能点,它为决策提供了重要数据支撑。随着小程序装修体系的上线后,首页,活动页更加的多样化,对于每个坑位的数据,以及后续的影响更加的重视。埋点数据采集对于促销活动的策略制定、及时调整及转化效果的验证都至关重要。
日志上报
上报方案
- 上报的接口采用POST,考虑到上报的数据非常多,GET并不适用;
- 上报方式,采用setTimeout,10秒后上报收集到的所有事件数据,并且清除定时器,当上报成功后重新开始定时器。
- 锁机制,日志必须带有用户,店铺或者其他必要属性才有意义,这时候需要等待这一类数据返回后,再解锁开始上报。
- 上报时机:小程序appShow事件是所有事件的开始 ,所以从这里开启定时器;appHide清除定时器,直接上报。
- 根据上报类型划分:批量上报,独立上报。大部分的事件都是走批量上报,但是存在一些特殊事件为了保证其准确性,及时性,降低丢失率使用独立上报。
const timer = (() => {
let _timer;
return {
start() {
if (_timer) {
return;
}
_timer = setTimeout(() => {
_timer = null;
// 上报
}, 10000);
},
clear() {
clearTimeout(_timer);
_timer = null;
},
};
})();
class ubCore {
constructor(){
// 事件数据集合
this.events = [];
// 锁
this.lock = true;
// 监听全部必要的属性 等到有值后解锁
EventBus.on('xx',(v)=>{
if(v){
this._unlock();
}
})
}
_unlock(){
/**
* 解锁
* 清空计时器
* 做一次手动上报
**/
}
add({eventkey, options}){
/**
*
* 收集事件到events集合
* 判断eventkey,appShow时开始定时器, appHide时关闭定时器,直接上报
**/
}
queueReport(){
/**
* 判断lock是否解锁 , 解锁后才允许上报
* 将events上报 成功后重新开启定时器
*
**/
}
report(){
// 直接上报 不通过定时器
}
}
工作流程
节点曝光埋点
曝光的定义及场景
定义:节点区域进入可视区域达到一定比例,也就是要进入到用户的视野范围内才算真正意义上的曝光,如果只是单纯的渲染出来并不算。
曝光的场景(坑位):页面内轮播的每一张图,热区图中的每个热区,热区轮播中的每张图下的不同热区,所有的商品列表下的每个商品等等。
解决方案
小程序内可以使用标准API监听元素与可见区域的相交变化。
wx.createIntersectionObserver().relativeToViewport().observe()
createIntersectionObserver
,创建观察者,设定触发回调比例
relativeToViewport
可以设定可视区域
observe
设定需要观察的节点和回调方法
优点:
- 可控的相交比例
- 回调触发会根据相交比例,可以避免不必要的执行。
class exposure {
// 根据API分析它需要的参数包括组件实例,目标节点,相交比例,回调函数
constructor(that, node, thresholds, callback) {
this.that = that;
this.node = node;
this.thresholds = thresholds;
this.callback = callback;
}
static creat_exposure(that, node, thresholds, callback) {
const Ep = new exposure(that, node, thresholds, callback);
Ep.createObserver();
}
createObserver() {
let obs = this.that.createIntersectionObserver({
thresholds: this.thresholds,
});
obs.relativeToViewport().observe(this.node, (res) => {
// res中可以获取该节点上dada-开头的属性数据,再进行事件的上报
...
// 回调
this.callback(res);
// 当目标节点曝光过后 停止监听 避免重复触发
obs.disconnect();
});
}
}
由于装修体系的存在,页面上的每个坑位都是没办法预先设定的,结合上诉,可以根据数据做处理,每个数据对应一个节点或者是一个坑位,统一节点命名规则,并将关键数据通过类似data-id属性挂载在节点上,这类属性是统一归纳出来,比如坑位名称,坑位号,坑位id等等,之前都是在wxml上配置,最后在获取数据后遍历每个数据根据命名规则进行监听。
tips:注意点setData必须和监听同步执行。
// wxml
<block wx:for="{{list}}" wx:key="index">
<view class="product-wrapper"
id='exp-{{item.id}}'
data-id="{{item.id}}">
</view>
</block>
// js
Promise.resolve()
.then(()=>{
this.setData({ productList })
productList.forEach((item) => {
exposure.creat_exposure(this, `#exp-${item.id}`, [0.7], (res) => {});
});
})
行为链路追溯
背景需求:用户在下单或者加入购物车这2个事件触发时,如果用户经过首页,活动页,需要知道用户是从哪个坑位进入的。
工作流程
在事件加入events集合时(ubCore.add
) 这一步骤中,额外建立一套新的流程 ,根据这些事件进行数据过滤,形成新的行为记录,并储存起来。
数据处理
几个问题
- 如何确保一个页面只有一个关于用户楼层坑位点击的数据
- 页面前后切换记录的增删
- 存在多个活动页,不同id情况下的记录处理
- 配置需要记录的页面路径
初始化过滤
大致分为几个步骤
- 根据接收到的页面生命周期pageShow做初始化过滤。 不管是页面还是前进还是后退,都会触发当前页面的pageShow事件,在这个节点做一个清洗操作【根据
getCurrentPages()
获取当前页面的路径和参数,在集合中查看是否有值,有值就删除记录,包括后面页面的记录】。因为A->B,B的记录一定是空,不会做处理。 只有当发生后退情况下D->C ,C页面的记录一定有值,并且处于C页面后只有2种情况(1)重新记录新的坑位 (2)继续后退,不管是哪种情况都可以把当前页面先进行记录清空。 - 根据制定的规则进行过滤,指定的页面,事件类型是elementClick,带有坑位标识,过滤出符合条件的数据后,在这个节点再做一次清洗操作【根据
getCurrentPages()
获取当前页面的路径和参数,在集合中查看是否有值,有值就删除记录,包括后面页面的记录】 ,最后放入集合。
异常情况
当坑位发生点击但是却没有发生跳转情况。
- 在D->C,C 在pageShow做初始化过滤时候需要将页面C记录和页面D记录一起删除。
- D页面已经存在记录,但是点击其他可跳转的坑位可以进行重新记录。