Vue3项目埋点方案完整实现,拿来就能用
做网站或者APP时,我们经常需要知道用户怎么用我们的产品。比如用户点了哪些按钮?在页面停留多久?有没有遇到错误?这些信息很重要,能帮我们改进产品。今天我就手把手教你给vue3项目做一个完整的埋点方案。
这个方案要实现三个主要功能:
记录用户点击了什么
计算用户在页面的停留时间
收集程序错误信息
为什么选择Vue3?
Vue3性能更好
Composition api用起来更灵活
TypeScript支持更完善
需要安装什么?
npm install vue-router@4我们不用第三方埋点库,自己用JavaScript实现,这样可以减少代码体积。
第一步:定义数据类型
先确定要收集哪些数据。新建一个文件 types/tracking.ts:// 点击事件
export interface ClickEvent {
type: 'click'
element: string // 点击的元素标识
page: string // 页面路径
timestamp: number // 时间戳
}
// 页面浏览事件
export interface PageViewEvent {
type: 'pageview'
page: string // 页面名称
duration: number // 停留时间(毫秒)
timestamp: number
}
// 错误事件
export interface ErrorEvent {
type: 'error'
message: string // 错误信息
stack?: string // 错误堆栈
page: string // 发生错误的页面
timestamp: number
}
// 所有事件类型
export type TrackingEvent = ClickEvent | PageViewEvent | ErrorEvent第二步:实现核心追踪类
这个类负责收集和发送数据。新建 utils/tracker.ts:import type { TrackingEvent } from '@/types/tracking'
class Tracker {
private queue: TrackingEvent[] = [] // 事件队列
private maxRetry = 3 // 最大重试次数
private batchSize = 10 // 批量发送大小
// 发送数据到服务器
private async sendToServer(events: TrackingEvent[]): Promise<void> {
try {
const response = await fetch('/api/track', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ events }),
})
if (!response.ok) {
throw new Error(`请求失败,状态码: ${response.status}`)
}
} catch (error) {
console.warn('埋点发送失败:', error)
throw error
}
}
// 添加事件
track(event: TrackingEvent): void {
this.queue.push(event)
// 达到批量大小就发送
if (this.queue.length >= this.batchSize) {
this.flush()
}
}
// 强制发送所有事件
async flush(): Promise<void> {
if (this.queue.length === 0) return
const events = [...this.queue]
this.queue = []
for (let attempt = 1; attempt <= this.maxRetry; attempt++) {
try {
await this.sendToServer(events)
break // 成功就退出
} catch (error) {
if (attempt === this.maxRetry) {
console.error('埋点发送最终失败:', error)
// 可以保存到localStorage,下次重试
this.saveToLocalStorage(events)
}
}
}
}
// 保存到本地存储(备用)
private saveToLocalStorage(events: TrackingEvent[]): void {
const stored = localStorage.getItem('pending_events') || '[]'
const pending = JSON.parse(stored)
const updated = [...pending, ...events]
localStorage.setItem('pending_events', JSON.stringify(updated.slice(-100))) // 最多存100条
}
}
// 创建单例
export const tracker = new Tracker()第三步:点击事件追踪指令
新建 directives/trackClick.ts:import { tracker } from '@/utils/tracker'
export const trackClick = {
mounted(el: htmlElement, binding: any) {
const trackData = binding.value
const handleClick = () => {
const event = {
type: 'click' as const,
element: trackData.element || el.tagName,
page: window.location.pathname,
timestamp: Date.now()
}
tracker.track(event)
}
el.addEventListener('click', handleClick)
// 保存引用,方便卸载
el._trackClickHandler = handleClick
},
unmounted(el: HTMLElement) {
if (el._trackClickHandler) {
el.removeEventListener('click', el._trackClickHandler)
delete el._trackClickHandler
}
}
}第四步:页面停留时间统计
新建 composables/usePageTrack.ts:import { onMounted, onUnmounted } from 'vue'
import { tracker } from '@/utils/tracker'
export function usePageTrack(pageName: string) {
let startTime = 0
onMounted(() => {
startTime = Date.now()
})
onUnmounted(() => {
const endTime = Date.now()
const duration = endTime - startTime
// 停留时间太短可能是误操作,过滤掉
if (duration < 1000) return
const event = {
type: 'pageview' as const,
page: pageName,
duration,
timestamp: endTime
}
tracker.track(event)
})
}第五步:全局错误捕获
新建 utils/errorHandler.ts:import { tracker } from './tracker'
import type { App } from 'vue'
export function setupErrorTracking(app: App): void {
// Vue错误
app.config.errorHandler = (err, vm, info) => {
const errorEvent = {
type: 'error' as const,
message: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined,
page: window.location.pathname,
timestamp: Date.now(),
component: info
}
tracker.track(errorEvent)
// 开发环境也打印到控制台
if (process.env.NODE_ENV !== 'production') {
console.error('Vue错误:', err, info)
}
}
// JavaScript错误
window.addEventListener('error', (event) => {
const errorEvent = {
type: 'error' as const,
message: event.message,
stack: event.error?.stack,
page: window.location.pathname,
timestamp: Date.now()
}
tracker.track(errorEvent)
})
// Promise错误
window.addEventListener('unhandledrejection', (event) => {
const errorEvent = {
type: 'error' as const,
message: event.reason?.message || '未处理的Promise错误',
stack: event.reason?.stack,
page: window.location.pathname,
timestamp: Date.now()
}
tracker.track(errorEvent)
})
}第六步:在Vue3中集成
修改 main.ts:import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { trackClick } from './directives/trackClick'
import { setupErrorTracking } from './utils/errorHandler'
const app = createApp(App)
// 注册全局指令
app.directive('track-click', trackClick)
// 设置错误追踪
setupErrorTracking(app)
// 定时发送埋点数据(30秒一次)
setInterval(() => {
import('@/utils/tracker').then(({ tracker }) => {
tracker.flush()
})
}, 30000)
app.use(router)
app.mount('#app')第七步:路由配置
修改 router/index.ts:import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue'),
meta: {
pageName: '首页'
}
},
{
path: '/about',
name: 'About',
component: () => import('@/views/About.vue'),
meta: {
pageName: '关于页面'
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 路由切换时记录
let pageStartTime = 0
router.beforeEach((to, from) => {
pageStartTime = Date.now()
})
router.afterEach((to) => {
const duration = Date.now() - pageStartTime
// 只记录有页面名称的路由
if (to.meta.pageName && duration > 1000) {
import('@/utils/tracker').then(({ tracker }) => {
tracker.track({
type: 'pageview',
page: to.meta.pageName as string,
duration,
timestamp: Date.now()
})
})
}
})
export default router第八步:在组件中使用
<!-- ProductList.vue -->
<template>
<div>
<h1>产品列表</h1>
<!-- 按钮点击追踪 -->
<button
v-track-click="{ element: 'filter-button' }"
@click="handleFilter"
>
筛选
</button>
<!-- 列表项点击追踪 -->
<div
v-for="product in products"
:key="product.id"
class="product-item"
v-track-click="{ element: `product-${product.id}` }"
@click="viewProduct(product)"
>
{{ product.name }}
</div>
</div>
</template>
<script setup lang="ts">
import { usePageTrack } from '@/composables/usePageTrack'
// 自动记录页面停留时间
usePageTrack('产品列表页')
const products = [
{ id: 1, name: '产品A' },
{ id: 2, name: '产品B' }
]
const viewProduct = (product: any) => {
// 处理产品查看
console.log('查看产品:', product.name)
}
</script>额外功能:性能监控
如果你还想监控页面性能,可以添加这个:// utils/performanceTracker.ts
import { tracker } from './tracker'
export function trackPerformance(): void {
// 页面加载完成时
window.addEventListener('load', () => {
const timing = performance.timing
const loadTime = timing.loadEventEnd - timing.navigationStart
tracker.track({
type: 'performance',
metric: 'page_load',
value: loadTime,
timestamp: Date.now(),
page: window.location.pathname
})
})
}优化建议
减少请求次数:批量发送数据,不要每个事件都单独请求
失败重试:网络不好时自动重试
数据采样:用户量很大时,可以只收集部分用户的数据
隐私保护:不要收集姓名、电话等个人信息
开关配置:提供开关让用户可以关闭数据收集
环境配置
创建配置文件 config/tracking.ts:export const trackingConfig = {
// 生产环境才发送真实数据
enabled: import.meta.env.PROD,
// 采样率:10%的用户
samplingRate: 0.1,
// 批量大小
batchSize: 20,
// 自动发送间隔(毫秒)
flushInterval: 30000,
// 是否开启调试
debug: import.meta.env.DEV
}总结
这个埋点方案包含了基本的数据收集功能,可以直接用到你的Vue3项目中。你可以根据自己的需求修改,比如添加更多的追踪类型,或者调整发送策略。关键点记住:
点击追踪用自定义指令
页面停留时间用生命周期钩子
错误捕获要全局设置
数据批量发送,减少请求
做好埋点后,你就能清楚地知道用户怎么用你的产品,为后续优化提供数据支持。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!