// 埋点信息分析
1. 埋点标识
- eventId:事件唯一标识,如:【立即购买】标识为'click/detail/buy'
- eventType:click点击事件、event访问事件
2. 埋点上报数据有哪些?
js
report(eventId, options)
interface options {
[key:string]: any // 自定义的事件信息
eventType // 埋点类型
userId // 用户ID(登录才有,未登录为空)
userAgent // 浏览器信息,内核信息,设备品牌/版本
netType // 网络信息,4G WIFI 5G
deviceId // 当前设备唯一ID
timestamp // 埋点时间,非上报时间
loacation // 页面地址
refer // 访问来源,如:百度/抖音/微信导流
}
3. 如何上报信息?
延时上报,在浏览器空闲时间上报信息,可以使用web worker / requestIdelCallback 等技术实现。
实现步骤:
ts
// 埋点
interface Params {
[key: string]: unknown
// eventType: string // 埋点类型
// userId: string // 用户ID(登录才有,未登录为空)
// userAgent: string // 浏览器信息,内核信息,设备品牌/版本
// netType: string // 网络信息,4G WIFI 5G
// deviceId: string // 当前设备唯一ID
// timestamp: number // 埋点时间,非上报时间
// loacation: string // 页面地址
// refer: string // 访问来源,如:百度/抖音/微信导流
}
let image: HTMLImageElement | null
interface Storage {
eventId: string
data: string
}
const queryString = (data: Params): string => {
return data + 'queryString' // 生成唯一ID
}
const uuid = () => {
return 'uuid' // 生成唯一ID
}
class BaseTrack {
constructor() {
if (window.addEventListener) {
// 页面卸载前/进入页面时,执行任务池
window.addEventListener('load', this.todoTrackPool, false)
window.addEventListener('beforeunload', this.todoTrackPool, false)
}
}
private ServerUrl = '//上报埋点的地址API'
// 记录埋点信息
track(eventId: string, params: Params | string, fromPool: boolean = false) {
if (!fromPool && typeof params !== 'string') { // 需要处理的埋点(直接调用)
const qs = queryString({
...params,
timestamp: Date.now(),
url: location.href,
userId: '', // 用户ID
deviceId: TrackStorage.getDeviceId()
})
const uniqueEventId = `${uuid()}_${eventId}}` // 埋点ID 增加唯一性,因为任务池可能有多个一样的event id
TrackStorage.pushStorage(uniqueEventId, qs)
this.report(uniqueEventId, qs)
} else if (typeof params === 'string') { // 任务池内未完成/上报失败的任务(非直接调用)
this.report(eventId, params)
}
}
// 上报埋点信息
report(uniqueEventId: string, qs: string, times: number = 3) {
const spend = () => {
image = null;
if (times > 0) {
this.report(uniqueEventId, qs, times - 1)
}
}
return new Promise(() => {
try {
const eventId = uniqueEventId.split('_')[1]
image = new Image()
image.onload = () => { // 上报成功
TrackStorage.removeStorage(uniqueEventId)
}
image.onerror = () => { // 上报失败,再次上报
spend()
}
image.src = this.ServerUrl + qs + `&eventId=${eventId}`
} catch (error) {
spend()
console.error(error);
}
}).catch((error) => {
console.error(error);
})
}
todoTrackPool() {
const Events: Storage[] = TrackStorage.getStorage() || []
Events.forEach((e: Storage) => {
this.track(e.eventId, e.data, false)
})
}
}
class TrackStorage {
private static readonly TRACK_KEY = 'track'
public static pushStorage(eventId: string, data: string) {
const Events: Storage[] = this.getStorage()
localStorage.setItem(this.TRACK_KEY, JSON.stringify(Events.concat({ eventId, data })))
}
public static removeStorage(eventId: string) {
const Events: Storage[] = this.getStorage()
localStorage.setItem(this.TRACK_KEY, JSON.stringify(Events.filter(e => e.eventId !== eventId)))
}
public static getStorage(): Storage[] {
const storage = localStorage.getItem(this.TRACK_KEY)
return storage ? JSON.parse(storage) : []
}
public static getDeviceId() { // 生成唯一的设备ID
try {
const key = 'device_id'
let device_id = localStorage.getItem(key)
if (!device_id) {
device_id = uuid()
localStorage.setItem(key, device_id)
}
return device_id
} catch (error) {
console.log(error);
return ''
}
}
}
const track = new BaseTrack()
track.track('click/buy', { nimei: '' })