文章目录
微信小程序组件化埋点实践
前言
在流量红利逐渐消失的现在,数据的采集、分析和精细化的运营显得更加重要,埋点能够帮助我们收集用户数据,辅助运营决策的同时提供算法以致力于建立用户图像,从而实现个性化推荐。
技术调研
-
组件埋点: 当前采用的埋点方案,利用组件实现埋点上传,业务代码和埋点逻辑基本抽离
优点:埋点逻辑与业务代码解耦,利于代码维护和复用。
缺点:小程序没有DOM元素,因此会增加DOM层级,组件内使用曝光埋点不便。 -
手动代码埋点:用户触发某个动作后手动上报数据
优点:准确,可以满足很多定制化的需求。
缺点:埋点逻辑与业务代码耦合到一起,不利于代码维护和复用。 -
无埋点:也叫“全埋点”,前端自动采集全部事件并上报埋点数据,在后端数据计算时过滤出有用数据
优点:收集用户的所有端上行为,很全面。
缺点:无效的数据很多,上报数据量大,服务器压力很大。
具体技术实现
点击埋点
由于小程序无法像REACT利用 prop.children,只能使用 slot 绑定实现点击埋点。
<slot bindtap="trackerClick"></slot>
// components/common/trackerClick/index.js
import {
appendQueue } from '../../../utils/report/reportQueue';
import {
formatDate } from '../../../utils/formatDate';
Component({
/**
* 虚拟化组件节点,防止影响 css 样式以及不增加 DOM 层级
*/
options: {
virtualHost: true
},
/**
* 组件的属性列表
*/
properties: {
event: {
type: String,
value: 'Click',
},
project: {
type: String,
value: '',
},
properties: {
type: Object,
value: null,
},
},
/**
* 组件的方法列表
*/
methods: {
trackerClick() {
const {
event, project, properties } = this.data;
let time = formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss.S');
// 后端字段要求时间格式化保留两位小数
time =
time.length >= 19
? time.slice(0, 19)
: `${
time.slice(0, 17)}0${
time.slice(17, 18)}`;
appendQueue('click', {
time,
event,
project,
properties,
});
},
},
});
曝光埋点
曝光埋点是最难实现的埋点,需要满足以下三点:
- 元素出现在视窗内一定的比例才算一次合法的曝光
- 元素在视窗内停留的时长达到一定的标准才算曝光
- 统计元素曝光时长
微信官方文档 – IntersectionObservers
由于微信小程序已支持 IntersectionObservers API,因此我们采用这个 API 实现曝光埋点。
不过由于小程序不具有 DOM 结构,没法像 REACT 传递 DOM 元素实现曝光埋点,并且组件内部的使用有一些需要特别注意的点。
<slot></slot>
import IntersectionObserver from '../../../utils/report/intersection-observer';
import {
appendQueue } from '../../../utils/report/reportQueue';
import {
formatDate } from '../../../utils/formatDate';
import CONFIG from '../../../utils/report/config';
Component({
/**
* 虚拟化组件节点,防止影响 css 样式以及不增加 DOM 层级
*/
options: {
virtualHost: true
},
/**
* 组件的属性列表
*/
properties: {
/**
* 上报的参数
*/
event: {
type: String,
value: 'View',
},
project: {
type: String,
value: 'baoyanmp',
},
properties: {
type: String,
},
/**
* IntersectionObserver属性
*/
// 监听的对象(组件内部需要使用组件的this属性)
context: {
type: Function,
value: null,
},
// 选择器
/**
selector类似于 CSS 的选择器,但仅支持下列语法。
ID选择器:#the-id
class选择器(可以连续指定多个):.a-class.another-class
子元素选择器:.the-parent > .the-child
后代选择器:.the-ancestor .the-descendant
跨自定义组件的后代选择器:.the-ancestor >>> .the-descendant
多选择器的并集:#a-node, .some-other-nodes
*/
selector: {
type: String,
},
// 相交区域,默认为页面显示区域
relativeTo: {
type: String,
value: null,
},
// 是否同时观测多个目标节点,动态列表不会自动增加
observeAll: {
type: Boolean,
value: false