Skip to content

埋点方案

概述

埋点是指在应用中埋入数据采集点,收集用户行为数据,用于分析用户行为、优化产品和评估效果。

埋点类型

1. 代码埋点

javascript
// 手动埋点
class Tracker {
  constructor(options = {}) {
    this.appId = options.appId
    this.userId = options.userId
    this.serverUrl = options.serverUrl || '/api/track'
  }

  // 追踪事件
  track(eventName, properties = {}) {
    const data = {
      appId: this.appId,
      userId: this.userId,
      event: eventName,
      properties,
      timestamp: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    }

    this.send(data)
  }

  // 页面浏览
  pageView(pageName, properties = {}) {
    this.track('page_view', {
      page_name: pageName,
      ...properties
    })
  }

  // 点击事件
  click(elementName, properties = {}) {
    this.track('click', {
      element_name: elementName,
      ...properties
    })
  }

  // 自定义事件
  custom(eventName, properties = {}) {
    this.track(eventName, properties)
  }

  // 发送数据
  send(data) {
    if (navigator.sendBeacon) {
      const blob = new Blob([JSON.stringify(data)], { type: 'application/json' })
      navigator.sendBeacon(this.serverUrl, blob)
    } else {
      fetch(this.serverUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
        keepalive: true
      })
    }
  }
}

// 使用示例
const tracker = new Tracker({
  appId: 'your-app-id',
  userId: 'user-123',
  serverUrl: '/api/track'
})

// 页面浏览
tracker.pageView('home', { source: 'navigation' })

// 按钮点击
tracker.click('submit-button', { form_type: 'login' })

// 自定义事件
tracker.custom('add_to_cart', {
  product_id: '123',
  product_name: 'iPhone',
  price: 999
})

2. 可视化埋点

vue
<template>
  <div>
    <button 
      v-track:click="{ event: 'submit_click', form_type: 'login' }"
      @click="handleSubmit"
    >
      提交
    </button>
    
    <div 
      v-track:view="{ event: 'banner_view', banner_id: '123' }"
      class="banner"
    >
      横幅广告
    </div>
  </div>
</template>

<script setup>
import { vTrack } from '@/directives/track'

function handleSubmit() {
  // 业务逻辑
}
</script>
javascript
// track.js
import { onMounted, onUnmounted } from 'vue'

export const vTrack = {
  mounted(el, binding) {
    const { arg, value } = binding
    
    if (arg === 'click') {
      el._trackClick = () => {
        tracker.track(value.event, value)
      }
      el.addEventListener('click', el._trackClick)
    }
    
    if (arg === 'view') {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            tracker.track(value.event, value)
            observer.unobserve(el)
          }
        })
      })
      observer.observe(el)
      el._observer = observer
    }
  },
  
  unmounted(el) {
    if (el._trackClick) {
      el.removeEventListener('click', el._trackClick)
    }
    if (el._observer) {
      el._observer.disconnect()
    }
  }
}

3. 无埋点(全埋点)

javascript
class AutoTracker {
  constructor(options = {}) {
    this.options = options
    this.tracker = new Tracker(options)
  }

  // 自动追踪所有点击
  trackAllClicks() {
    document.addEventListener('click', (e) => {
      const target = e.target
      
      // 获取元素信息
      const elementInfo = this.getElementInfo(target)
      
      this.tracker.track('auto_click', elementInfo)
    }, true)
  }

  // 自动追踪页面浏览
  trackAllPageViews() {
    // 初始页面
    this.tracker.pageView(document.title)

    // 监听路由变化
    const originalPushState = history.pushState
    const self = this
    
    history.pushState = function(...args) {
      originalPushState.apply(history, args)
      self.tracker.pageView(document.title)
    }

    window.addEventListener('popstate', () => {
      this.tracker.pageView(document.title)
    })
  }

  // 获取元素信息
  getElementInfo(element) {
    return {
      tag_name: element.tagName.toLowerCase(),
      class_name: element.className,
      id: element.id,
      text: element.innerText?.slice(0, 50),
      xpath: this.getXPath(element),
      href: element.href,
      src: element.src
    }
  }

  // 获取 XPath
  getXPath(element) {
    if (element.id) {
      return `//*[@id="${element.id}"]`
    }

    const parts = []
    let current = element

    while (current && current.nodeType === Node.ELEMENT_NODE) {
      let index = 0
      let sibling = current.previousSibling

      while (sibling) {
        if (sibling.nodeType === Node.ELEMENT_NODE && sibling.tagName === current.tagName) {
          index++
        }
        sibling = sibling.previousSibling
      }

      const tagName = current.tagName.toLowerCase()
      const indexStr = index ? `[${index + 1}]` : ''
      parts.unshift(`${tagName}${indexStr}`)
      current = current.parentNode
    }

    return '/' + parts.join('/')
  }

  // 初始化
  init() {
    this.trackAllClicks()
    this.trackAllPageViews()
  }
}

埋点规范

1. 事件命名规范

javascript
// 事件名称格式:模块_动作
const events = {
  // 页面浏览
  page_view: '页面浏览',
  
  // 点击事件
  button_click: '按钮点击',
  link_click: '链接点击',
  
  // 表单事件
  form_submit: '表单提交',
  form_reset: '表单重置',
  
  // 电商事件
  product_view: '商品浏览',
  add_to_cart: '加入购物车',
  remove_from_cart: '移出购物车',
  purchase: '购买',
  
  // 搜索事件
  search: '搜索',
  search_result_click: '搜索结果点击'
}

2. 属性命名规范

javascript
// 属性名称使用下划线命名法
const properties = {
  // 通用属性
  page_name: '页面名称',
  page_url: '页面URL',
  referrer: '来源页面',
  
  // 用户属性
  user_id: '用户ID',
  user_name: '用户名',
  user_type: '用户类型',
  
  // 商品属性
  product_id: '商品ID',
  product_name: '商品名称',
  product_category: '商品分类',
  product_price: '商品价格',
  
  // 事件属性
  event_duration: '事件持续时间',
  event_result: '事件结果'
}

3. 数据格式规范

javascript
{
  // 必填字段
  event: 'event_name',        // 事件名称
  timestamp: 1234567890000,   // 时间戳
  
  // 用户信息
  user_id: 'user-123',        // 用户ID
  device_id: 'device-456',    // 设备ID
  
  // 环境信息
  app_id: 'your-app-id',      // 应用ID
  app_version: '1.0.0',       // 应用版本
  platform: 'web',            // 平台
  os: 'Windows',              // 操作系统
  browser: 'Chrome',          // 浏览器
  
  // 页面信息
  page_name: 'home',          // 页面名称
  page_url: 'https://...',    // 页面URL
  referrer: 'https://...',    // 来源页面
  
  // 事件属性
  properties: {
    // 具体属性
  }
}

最佳实践

1. 埋点设计

  • 明确埋点目标
  • 定义清晰的指标
  • 保持埋点一致性

2. 数据质量

  • 数据校验
  • 异常处理
  • 数据去重

3. 性能优化

  • 批量上报
  • 数据压缩
  • 离线缓存

相关资源

基于 VitePress 的本地知识库