Vue3项目埋点方案完整实现,拿来就能用

更新日期: 2025-12-02 阅读: 18 标签: 埋点

网站或者APP时,我们经常需要知道用户怎么用我们的产品。比如用户点了哪些按钮?在页面停留多久?有没有遇到错误?这些信息很重要,能帮我们改进产品。今天我就手把手教你给vue3项目做一个完整的埋点方案。

这个方案要实现三个主要功能:

  1. 记录用户点击了什么

  2. 计算用户在页面的停留时间

  3. 收集程序错误信息


为什么选择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
    })
  })
}


优化建议

  1. 减少请求次数:批量发送数据,不要每个事件都单独请求

  2. 失败重试:网络不好时自动重试

  3. 数据采样:用户量很大时,可以只收集部分用户的数据

  4. 隐私保护:不要收集姓名、电话等个人信息

  5. 开关配置:提供开关让用户可以关闭数据收集


环境配置

创建配置文件 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项目中。你可以根据自己的需求修改,比如添加更多的追踪类型,或者调整发送策略。

关键点记住:

  • 点击追踪用自定义指令

  • 页面停留时间用生命周期钩子

  • 错误捕获要全局设置

  • 数据批量发送,减少请求

做好埋点后,你就能清楚地知道用户怎么用你的产品,为后续优化提供数据支持。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13255

前端监控和前端埋点方案设计

在线上项目中,需要统计产品中用户行为和使用情况,从而可以从用户和产品的角度去了解用户群体,从而升级和迭代产品,使其更加贴近用户。用户行为数据可以通过前端数据监控的方式获得,除此之外,前端还需要实现性能监控和异常监控

vue项目前端埋点

埋点方案的确定,业界的埋点方案主要分为以下三类:代码埋点:在需要埋点的节点调用接口,携带数据上传。如百度统计等;可视化埋点:使用可视化工具进行配置化的埋点,即所谓的「无痕埋点」

200行代码实现前端无痕埋点

什么是无痕埋点?简单来说,就是当引入无痕埋点的库以后,用户在浏览器里所有行为和操作都会被自动记录下来,并将信息发送到后端进行统计和分析

前端埋点之曝光实现

最近有一个工作需求是曝光埋点,让我得以有机会接触相关的东西。之前实习时没有做过这方面的需求,个人项目更是和埋点扯不上关系。以至于上周开会讨论时听到“埋点”这个词就怂了。

数据埋点的艺术

定义数据埋点及其交接主要分为四个部分,梳理数据需求—定义数据指标—埋点整理—文档输出——埋点验收,前两个步骤在上文中已经详细描述过方法,本文不再赘述。本文较为简洁,整理了梳理埋点的方法和与开发交接的方法

vue项目埋点

通过可视化交互的手段,代替代码埋点。将业务代码和埋点代码分离,提供一个可视化交互的页面,输入为业务代码,通过这个可视化系统,可以在业务代码中自定义的增加埋点事件等等

前端异常埋点系统初探

开发者有时会面临上线的生产环境包出现了异常:bug: ,在长期生产bug并修复bug的循环中总结出一下几个痛点:无法快速定位到发生错误的代码位置,因为脚手架构建时会用webapck自动帮我们压缩代码

前端埋点sdk封装

前端埋点sdk的方案十分成熟,之前用的都是公司内部统一的埋点产品,从前端埋点和数据上报后的可视化查询全链路打通。但是在最近的一个私有化项目中就遇到了问题,因为服务都是在客户自己申请的服务器上的,需要将埋点数据存放到自己的数据库中

在 Vue3 中进行点击事件埋点

如何在 Vue 中对每个点击事件插入一个函数?由于 .vue 文件是将 <template>、<script> 和 <style> 分开进行单独解析,所以不能通过 babel 将监听函数准确解析出来

前端监控的搭建步骤,别再一头雾水了!

在动手实现之前,首先脑子里要有一个整体脉络,明白搭建前端监控具体的流程步骤有哪些。因为前端监控系统实际上是一个完整的全栈项目,而并不仅仅是前端,甚至主要的实现都是围绕在数据方面的。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!