埋点方案实现

// 埋点信息分析

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: '' })
相关推荐
gnip1 小时前
Jst执行上下文栈和变量对象
前端·javascript
excel1 小时前
🐣 最简单的卷积与激活函数指南(带示例)
前端
醉方休2 小时前
npm/pnpm软链接的优点和使用场景
前端·npm·node.js
拉不动的猪2 小时前
简单回顾下Weakmap在vue中为何不能去作为循环数据源,以及替代方案
前端·javascript·vue.js
How_doyou_do2 小时前
数据传输优化-异步不阻塞处理增强首屏体验
开发语言·前端·javascript
奇舞精选2 小时前
超越Siri的耳朵:ASR与Whisper零代码部署实战指南
前端·人工智能·aigc
奇舞精选2 小时前
Nano Banana 如何为前端注入 AI 控制力
前端·aigc
一支鱼2 小时前
基于 Node.js 的短视频制作神器 ——FFCreator
前端·node.js·音视频开发
DT——2 小时前
前端登录鉴权详解
前端·javascript
李姆斯3 小时前
复盘上瘾症:到底什么时候该“复盘”,什么时候不需要“复盘”
前端·后端·团队管理