埋点方案实现

// 埋点信息分析

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: '' })
相关推荐
像风一样自由202034 分钟前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
aiprtem1 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊1 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术1 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing1 小时前
0704-0706上海,又聚上了
前端·新浪微博
止观止2 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall2 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴2 小时前
简单入门Python装饰器
前端·python
袁煦丞3 小时前
数据库设计神器DrawDB:cpolar内网穿透实验室第595个成功挑战
前端·程序员·远程工作
天天扭码3 小时前
从图片到语音:我是如何用两大模型API打造沉浸式英语学习工具的
前端·人工智能·github