埋点方案实现

// 埋点信息分析

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: '' })
相关推荐
阿珊和她的猫23 分钟前
v-scale-scree: 根据屏幕尺寸缩放内容
开发语言·前端·javascript
加班是不可能的,除非双倍日工资5 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip6 小时前
vite和webpack打包结构控制
前端·javascript
excel6 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼6 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT7 小时前
promise & async await总结
前端
Jerry说前后端7 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化