微信小程序《埋点系统设计》
站长
· 阅读数 18
简介
现在的小程序埋点的方案有很多优秀的企业提供了平台,比如阿拉丁小程序统计、微信官方的 WE
分析,而这些有个共同点就是需要在相应的后台中查看,而且这些只能满足所有行业产品中一些基本的的数据埋点。当你的运营脑门开始增大时,你就会意识到这些平台已经满足不了,这时候就需要我们自己写一个埋点系统
一. 埋点系统
埋点系统是业务闭环一个关键点,任何产品都是需要运营的,而运营需要根据这个产品的各种使用情况来分析产品的拓展或优化,所以埋点系统都会包含以下几点:
- 用户在某个页面的访问、点击、分享次数
- 用户在某个功能的点击、使用次数
- 用户的行为轨迹
二. 微信小程序埋点设计
站在用户的角度上可以分析出微信小程序收集的方式有两种:
- 生命周期:用户打开小程序或进入页面时通过生命周期函数自动收集
- 自定义事件:用户触发某次点击时通过自定义事件去收集
2.1 前端埋点 SDK
设计思路
- 入口模块:负责与小程序进行通讯、负责把定义好的自定义事件导出给小程序使用、负责把数据传递给数据收集模块
- 自定义事件模块:负责定义好业务拓展需要的自定义事件模块
- 数据收集模块:负责定义好服务端需要的数据接口并上报给服务端
三. 入口模块(index.js)
入口模块首先要拦截 App
、Page
的生命周期函数,然后插入自己的埋点代码,这里需要注意的是 page
的 onShareAppMessage
事件,它通常会有返回值
import { gatherOrDispatchStat } from './stat'
import { statClick } from './event'
const app = App // 小程序全局实例
const page = Page // 小程序页面实例
/**
* 拦截 Page | App 实例的方法
* @param {Object} instanceData Page | App 实例初始化传入的 option
* @param {String} name option 的 key
* @param {Function} callback 回调函数
*/
const _handleInstanceMethod = function(instanceData, name, callback) {
if (instanceData[name]) {
const e = instanceData[name]
instanceData[name] = function(arg) {
callback.call(this, arg)
e.call(this, arg)
}
} else {
instanceData[name] = function(arg) {
callback.call(this, arg)
}
}
}
// 拦截 App
App = function (appData) {
_handleInstanceMethod(appData, 'onLaunch', function(e) {
gatherOrDispatchStat({
pagePath: e.path,
eventName: '小程序启动'
})
})
app(appData)
}
// 拦截 Page
Page = function (pageData) {
const shareFunction = pageData.onShareAppMessage
_handleInstanceMethod(pageData, 'onShow', function(e) {
gatherOrDispatchStat({
eventName: '进入页面'
})
})
/**
* 分享事件比较特殊,因为它有返回值
*/
if (shareFunction) {
pageData.onShareAppMessage = function(e) {
let shareParam = shareFunction.call(this, e)
gatherOrDispatchStat({
eventName: '页面分享'
})
return shareParam || {}
}
}
page(pageData)
}
// 暴露一些自定义事件
export default {
statClick
}
四. 数据模块(stat.js)
数据模块负责收集和派发数据,一般用户操作速度较快,如果每收集一个数据都向服务端上报的话会产生性能问题,所以我们可以做一个节流处理,或者是合并日志请求
// 生成 uuid
const _generatorUuid = () => {
let uuid = wx.getStorageSync('stat_uuid')
if (!uuid) {
uuid = `user_${Date.now()}`
wx.setStorageSync('stat_uuid', uuid)
}
return uuid
}
// 埋点数据结构定义
const statData = {
time: '', // 日志收集时间
uuid: _generatorUuid(), // 当前设备id
pagePath: '', // 小程序路径
eventName: '', // 事件名称
}
const duration = 5000 // 间隔
let timer = null // 延时器
let time = 0 // 最后一次上报时间
let statList = [] // 日志收集缓存
/**
* 收集并派发函数
* @param {Object} param 收集数据
*/
export const gatherOrDispatchStat = (param) => {
const pages = getCurrentPages()
const data = { ...statData, ...param }
const now = Date.now()
data.time = now.toString()
data.pagePath = pages[pages.length - 1]?.route || param.pagePath || ''
// 避免多次请求频繁发送请求,做个节流处理
if (now - time > duration) {
const patchData = [data, ...statList]
statList = []
time = now
clearTimeout(timer)
timer = setTimeout(() => {
/**
* ...这里省略派发日志操作
*/
console.log(patchData);
}, duration)
} else {
statList.push(data)
}
}
五. 自定义事件模块(event.js)
自定义事件的业务场景实在太多了,特别是点击派发的事件,比如订阅、购买商品、点赞等,自定义事件需要保留拓展的行为,设计一定要保持灵活性,毕竟运营需要统计的需求会不断增加
import { gatherOrDispatchStat } from './stat'
/**
* 点击事件
* @param {String} eventName 事件名称
*/
export const statClick = (eventName) => {
gatherOrDispatchStat({
eventName,
})
}
总结
上面的代码只是我个人模拟的一些场景,只为了提供思路,这些代码可以复制到微信开发工具运行,使用的方式为以下:
// app.js
import statInstance from './utils/stat/index'
App({
globalData: {
statInstance,
}
})
// pages/index/index.js
const app = getApp()
Page({
onclick() {
app.globalData.statInstance.statClick('点击事件')
}
})