Appearance
埋点方案
概述
埋点是指在应用中埋入数据采集点,收集用户行为数据,用于分析用户行为、优化产品和评估效果。
埋点类型
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. 性能优化
- 批量上报
- 数据压缩
- 离线缓存