1. 方案概述
1.1 埋点类型
- 手动埋点:开发人员在代码中显式调用埋点方法。
- 自动埋点:通过监听全局事件自动触发埋点。
- 可视化埋点:可视化埋点。
- 用户离开页面埋点:记录用户离开页面的行为。
1.2 默认埋点信息
默认埋点信息包括:
- 页面信息:页面 URL、页面标题、页面加载时间。
- 设备信息:设备类型、操作系统、浏览器类型、屏幕分辨率。
- 环境信息:网络类型、语言设置、时区。
2. 默认埋点信息设计
2.1 默认埋点信息
以下是可以直接获取的默认埋点信息:
const defaultTrackingData = {
// 页面信息
pageUrl: window.location.href, // 页面 URL
pageTitle: document.title, // 页面标题
pageLoadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart, // 页面加载时间
// 设备信息
deviceType: navigator.userAgent, // 设备类型
os: navigator.platform, // 操作系统
browser: navigator.userAgent, // 浏览器类型
screenResolution: `${window.screen.width}x${window.screen.height}`, // 屏幕分辨率
// 环境信息
networkType: navigator.connection?.effectiveType || 'unknown', // 网络类型
language: navigator.language, // 语言设置
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, // 时区
};
3. 埋点工具类实现
3.1 核心工具类
Tracker
类负责埋点的核心逻辑,包括默认埋点信息的获取、数据合并和发送。
class Tracker {
constructor() {
this.pageEnterTime = Date.now(); // 记录页面进入时间
this.trackPageLeave(); // 监听页面离开事件
}
// 监听页面离开事件
trackPageLeave() {
window.addEventListener('beforeunload', () => {
const pageStayTime = Date.now() - this.pageEnterTime; // 计算页面停留时间
this.track('page_leave', {
pageStayTime,
leaveType: 'close_or_refresh', // 离开方式:关闭或刷新页面
});
});
window.addEventListener('unload', () => {
const pageStayTime = Date.now() - this.pageEnterTime; // 计算页面停留时间
this.track('page_leave', {
pageStayTime,
leaveType: 'navigate', // 离开方式:跳转到其他页面
});
});
}
// 手动埋点
track(eventType, customData = {}) {
const trackingData = this.mergeTrackingData({
eventType,
...customData,
});
this.sendTrackingData(trackingData);
}
// 合并默认信息和自定义信息
mergeTrackingData(customData = {}) {
return {
pageUrl: window.location.href,
pageTitle: document.title,
pageLoadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart,
deviceType: navigator.userAgent,
os: navigator.platform,
browser: navigator.userAgent,
screenResolution: `${window.screen.width}x${window.screen.height}`,
networkType: navigator.connection?.effectiveType || 'unknown',
language: navigator.language,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
...customData,
timestamp: new Date().toISOString(),
};
}
// 发送埋点数据
sendTrackingData(data) {
console.log('Tracking Data:', data);
// 示例:fetch('/api/track', { method: 'POST', body: JSON.stringify(data) });
}
}
// 使用示例
const tracker = new Tracker();
tracker.track('button_click', { buttonId: 'submit_button' });
4. 自动埋点实现
4.1 自动埋点类
AutoTracker
类继承自 Tracker
,负责监听全局事件并自动触发埋点。
class AutoTracker extends Tracker {
constructor(config) {
super();
this.config = config;
this.init();
}
// 初始化自动埋点
init() {
this.trackClicks();
this.trackScrolls();
this.trackInputs();
}
// 监听点击事件
trackClicks() {
document.addEventListener('click', (event) => {
const target = event.target;
const elementPath = this.getElementPath(target);
if (this.shouldTrackElement(target, 'click')) {
const eventData = this.mergeTrackingData({
eventType: 'click',
eventTarget: elementPath,
});
this.sendTrackingData(eventData);
}
});
}
// 监听滚动事件
trackScrolls() {
let lastScrollTime = 0;
window.addEventListener('scroll', () => {
const currentTime = new Date().getTime();
// 防抖处理,避免频繁触发
if (currentTime - lastScrollTime > 1000) {
lastScrollTime = currentTime;
const eventData = this.mergeTrackingData({
eventType: 'scroll',
scrollY: window.scrollY,
});
this.sendTrackingData(eventData);
}
});
}
// 监听输入事件
trackInputs() {
document.addEventListener('input', (event) => {
const target = event.target;
const elementPath = this.getElementPath(target);
if (this.shouldTrackElement(target, 'input')) {
const eventData = this.mergeTrackingData({
eventType: 'input',
eventTarget: elementPath,
value: target.value,
});
this.sendTrackingData(eventData);
}
});
}
// 获取元素的路径
getElementPath(element) {
const path = [];
while (element && element.nodeName.toLowerCase() !== 'body') {
let selector = element.nodeName.toLowerCase();
if (element.id) {
selector += `#${element.id}`;
} else if (element.className) {
selector += `.${element.className.split(' ').join('.')}`;
}
path.unshift(selector);
element = element.parentNode;
}
return path.join(' > ');
}
// 判断元素是否需要埋点
shouldTrackElement(element, eventType) {
const elementPath = this.getElementPath(element);
// 检查埋点配置
for (const [page, config] of Object.entries(this.config.pages)) {
if (window.location.pathname === page) {
if (config.excludeComponents.includes(elementPath)) {
return false; // 禁止埋点
}
if (config.events.includes(eventType)) {
return true; // 允许埋点
}
}
}
return false;
}
}
// 使用示例
const trackingConfig = {
pages: {
'/home': {
events: ['click', 'scroll', 'input'],
excludeComponents: ['header'],
},
'/product/:id': {
events: ['click', 'scroll'],
excludeComponents: [],
},
},
};
const autoTracker = new AutoTracker(trackingConfig);
5. 配置管理
5.1 前端配置
在前端项目中,通常会有一个配置文件来管理埋点信息。
const trackingConfig = {
pages: {
'/home': {
events: ['click', 'scroll', 'input'],
excludeComponents: ['header'],
},
'/product/:id': {
events: ['click', 'scroll'],
excludeComponents: [],
},
},
};
5.2 接口获取配置
后端提供一个接口,用于动态获取埋点配置。
async function fetchTrackingConfig() {
try {
const response = await fetch('/api/tracking-config');
const config = await response.json();
return config;
} catch (error) {
console.error('Failed to fetch tracking config:', error);
return null;
}
}
6. 总结
本文档提供了一套完整的埋点方案,包括:
- 默认埋点信息:涵盖页面、设备和环境信息。
- 手动埋点:支持显式调用埋点方法。
- 自动埋点:监听全局事件,自动触发埋点。
- 用户离开页面埋点:记录页面停留时间和离开方式。