分享如何实现前端埋点工程

这篇文章主要是分享一下在实际项目中如何做好前端埋点。前端埋点相关的知识点,在面试中也会经常被遇到。这篇将会从技术方案设计到最终实现给出简易版代码。

什么是埋点?为什么项目中需要埋点?

埋点主要是用来收集用户行为的数据,在项目开发中,我们会在前端代码中插入代码或者脚本的方式实现埋点功能。

埋点的作用主要是为了在应用中特定流程收集一些信息及关键业务数据,用来追踪应用使用的状况,以便后续进一步优化产品提供运营数据的支撑。例如某一功能的访问数、访客数、停留时间时长等

实现埋点的方案

  1. 手动埋点:也就是代码埋点,需要上报的时候写js代码调用接口上报。
    弊端:在业务中混入了大量的埋点,对业务代码有破坏性,甚至会因为埋点报错导致页面崩溃。

  2. 声明式埋点:在dom匀速上写入点位,然后自动上报。目前主流的技术方案。它的思路是将埋点代码和具体的交互业务逻辑解耦,开发者只需关心需要埋点的空间,并且为这些控件声明需要埋点的数据即可,降低埋点成本。

  3. 可视化埋点:通过工具界面需要采集的元素和事件、可不用手写代码。目前开源的有mixpanel

声明式埋点实现逻辑

首先就是在应用启动后的初始化埋点,初始化之后,便是埋点系统的三大核心监听事件了。
1️⃣是监听页面dom的变化,dom新增或销毁时,上报给服务器;2️⃣是监听页面的点击事件,当我们定义的d位的dom被点击时,上报服务器3️⃣监听页面关闭,当页面关闭时,找出所有埋点dom,上报服务器。
截屏2026-02-03 16.26.09

代码实现

接下来开始编写我们的代码,在写代码之前,我们应该先要了解一下点位的概念

点位:
点位是我们上报给接口的东西,我们通过这个点位就能知道它代码的是什么。

下图中的示例中,我使用data-tp的自定义属性来设置埋点dom,用data-tp-data来传递埋点上报时的数据

截屏2026-02-03 17.51.07

接下来进入到js的编写,一般情况下,我们可以单独封装js,这样避免和业务逻辑的混淆。

import server from './server'
// 前端 埋点上报
const reportServer = (doms:any,dataTp='') => {
    server.post('/user/tp/report',{
        domMap:doms,
        dataTp:dataTp
    })
}

const domMap:any= {}
const hasRepeat= (tp:string) => {
    return domMap[tp]?.log || false
}

const reportAll = () => {
    document.querySelectorAll('data-tp').forEach((item) => {
        const dataTP = item?.getAttribute?.('data-tp')
        const dataVal = item?.getAttribute?.('data-tp-data') || ''
        reportServer(dataTP,dataVal)
    })
}

// 监听dom节点变化
const initObserver = () => {
    const observer = new MutationObserver((mutation) => {
        console.log(mutation,'mutation');
        mutation.forEach((item) => {
            // 当页面销毁时,也可以选择在页面节点新增时,需要将removedNodes改为addedNodes
            if (item.type == 'childList' && item.removedNodes?.length > 0) {
                    item.removedNodes.forEach((ritem:any) => {
                        if (ritem?.getAttribute?.('data-tp')) {
                            const dataTP = ritem?.getAttribute('data-tp')
                            const dataVal = ritem?.getAttribute('data-tp-data')
                            if (hasRepeat(dataTP)) return 
                            reportServer(dataTP,dataVal)
                            domMap[dataTP] = {
                                log:true,
                                data:dataVal
                            }
                        }
                    })
            }
        })
    })
    observer.observe(document.body,{
        subtree:true,
        childList:true,
    })
}
// 监听所有的点击事件
const initClickObserver = () => {
    window.addEventListener('click',(e) => {
        const target = e.target as Element
        const dataTP = target?.getAttribute?.('data-tp')
        const dataVal = target?.getAttribute?.('data-tp-data') || ''
        reportServer(dataTP,dataVal)
    })
}
// 监听浏览器页签关闭/切换时
const initPageChangeObserver = () => {
    window.addEventListener('visibilityChange',() => {
        reportAll()
    })
}


const initTrackPort = () => {
    initObserver()
    initClickObserver()
    initPageChangeObserver()
}

initTrackPort()

上述的便是在项目中可实现的埋点简易版代码,实际的代码还需要根据业务需求调整

posted @ 2026-02-03 20:07  前端加油站  阅读(6)  评论(0)    收藏  举报