教你怎么前端实现埋点上报

65 篇文章 2 订阅

前言

那么在一个企业中,我们要如何去了解用户呢?最直接有效的方式就是了解用户的行为,了解用户在网站中做了什么,呆了多久。而如何去实现这一操作,这就涉及到我们前端的埋点了。

一.埋点方式

在聊如何进行埋点前,我们先介绍下什么是埋点?

所谓'埋点'是数据采集领域(尤其是用户行为数据采集领域)的术语,指的是针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。. 比如用户某个icon点击次数、观看某个视频的时长等等。从数据产品经理视角,聊聊埋点的意义 | 人人都是产品经理 (woshipm.com)

基于此我们可以知道埋点是实际上是对特定事件或者行为的数据监控和上报,常见的埋点上报方式有ajax,img,navigator.sendBeacon下面介绍下这三种埋点上报方式

(1)基于ajax的埋点上报 

介绍

因为埋点实际上是对关键节点的数据进行上报是和服务端交互的一个过程,所以我们可以和后端约定一个接口通过ajax去进行数据上报。

代码实现

我们可以封装一个方法,代码如下:

function buryingPointAjax(data) {
  return new Promise((resolve, reject) => {
    // 创建ajax请求
    const xhr = new XMLHttpRequest();
    // 定义请求接口
    xhr.open("post", '/buryingPoint', true);
    // 发送数据
    xhr.send(data);
  });
}

使用时,直接调用即可

let info = {}
buryingPointAjax(info) // 这样就成功上报了info的对象

缺点

一般而言,埋点域名并不是当前域名,因此请求会存在跨域风险,且如果ajax配置不正确可能会浏览器拦截。因此使用ajax这类请求并不是万全之策。

(2)基于img的埋点上报

上面可以看到如果使用ajax的话,会存在跨域的问题。而且数据上报前端主要是负责将数据传递到后端,并不过分强调前后端交互。因此我们可以通过一些支持跨域的标签去实现数据上报功能。

script,link,img就是我们上报的数据的最好对象

先说结论,这里推荐使用img标签去实现。

script及link的缺陷

因为埋点涉及到请求,因此我们需要保证script和link标签的src可以正常请求。如果需要请求script和link,我们需要将标签挂载到页面上。

验证缺陷

不妨验证下,我们在管理台中加入以下代码:

let a = document.createElement('script')
a.src = 'https://lf-headquarters-speed.yhgfb-cn-static.com/obj/rc-client-security/web/stable/1.0.0.28/bdms.js'

 创建一个script标签,未挂载中页面上,并不会发起请求

书接上文,当我们将这个标签挂载中页面上时:

document.body.appendChild(a)

 这时发起了请求

结论

当我们使用script和link进行埋点上报时,需要挂载到页面上,而反复操作dom会造成页面性能受影响,而且载入js/css资源还会阻塞页面渲染,影响用户体验,因此对于需要频繁上报的埋点而言,script和link并不合适。

 

 基于img做埋点上报

基于img做埋点上报

通常使用img标签去做埋点上报,img标签加载并不需要挂载到页面上,基于js去new image(),设置其src之后就可以直接请求图片。

验证img优势

控制台去创建一个image标签,如下:

var img=new Image();
img.src="https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web/img/MaskGroup.13dfc4f1.png";

可以看到即便未被挂载到页面上依旧发起了请求。

结论

因此当我们做埋点上报时,使用img是一个不错的选择。

  1. img兼容性好

  2. 无需挂载到页面上,反复操作dom

  3. img的加载不会阻塞html的解析,但img加载后并不渲染,它需要等待Render Tree生成完后才和Render Tree一起渲染出来

注:通常埋点上报会使用gif图,合法的 GIF 只需要 43 个字节

 

(3)基于Navigator.sendBeacon的埋点上报

Navigator.sendBeacon是目前通用的埋点上报方案,Navigator.sendBeacon方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等等)。

介绍

navigator.sendBeacon()  方法可用于通过 HTTP POST 将少量数据 异步 传输到 Web 服务器。

作用

它主要用于将统计数据发送到 Web 服务器,同时避免了用传统技术(如:XMLHttpRequest)发送分析数据的一些问题。

补充

sendBeacon 如果成功进入浏览器的发送队列后,会返回true;如果受到队列总数、数据大小的限制后,会返回false。返回ture后,只是表示进入了发送队列,浏览器会尽力保证发送成功,但是否成功了,不会再有任何返回值。

例子

以掘金为例:

 这里发了一个post请求,将小量的数据发到服务端,用于统计数据

优势

相较于img标签,使用navigator.sendBeacon会更规范,数据传输上可传输资源类型会更多。

对于ajax在页面卸载时上报,ajax有可能没上报完,页面就卸载了导致请求中断,因此ajax处理这种情况时必须作为同步操作.

sendBeacon是异步的,不会影响当前页到下一个页面的跳转速度,且不受同域限制。这个方法还是异步发出请求,但是请求与当前页面脱离关联,作为浏览器的任务,因此可以保证会把数据发出去,不拖延卸载流程。

 

注意

该方法在支付宝中可能会被拦截,如果发现支付宝数据上报异常,可以尝试排查这块。

总结

前端埋点上报常使用ajax,img,navigator.sendBeacon。不推荐使用ajax。如果考虑兼容性的话,img是不二之选。目前最合适的方案是navigator.sendBeacon,不仅是异步的,而且不受同域限制,而且作为浏览器的任务,因此可以保证会把数据发出去,不影响页面卸载。

二.常见埋点行为

(1)点击触发埋点

绑定点击事件,当点击目标元素时,触发埋点上报。

function clickButton(url, data) {
    navigator.sendBeacon(url, data)
}

(2)页面停留时间上报埋点

路由文件中,初始化一个startTime,当页面离开时通过路由守卫计算停留时间。

let url = ''// 上报地址
let startTime = Date.now()
let currentTime = ''
router.beforeEach((to, from, next) => { 
     if (to) {
         currentTime = Date.now()
         stayTime = parseInt(currentTime - startTime)
         navigator.sendBeacon(url, {time: stayTime})
         startTime = Date.now()
     }
 })

(3)错误监听埋点

通过监听函数去接收错误信息。

1.vue错误捕获
app.config.errorHandler = (err) => { 
    navigator.sendBeacon(url, {error: error.message, text: 'vue运行异常' })
}
2.JS异常与静态资源加载异常
window.addEventListener('error', (error) => { 
    if (error.message) { 
        navigator.sendBeacon(url, {error: error.message, text: 'js执行异常' })
    } else { 
        navigator.sendBeacon(url, {error: error.filename, text: '资源加载异常' })
    } 
}, true)
3.请求错误捕获
axios.interceptors.response.use(
  (response) => {
    if (response.code == 200) {
      return Promise.resolve(response);
    } else {
      return Promise.reject(response);
    }
  },
  (error) => {
    // 返回错误逻辑
    navigator.sendBeacon(url, {error: error, text: '请求错误异常' })
  }
);
4.内容可见埋点

通过交叉观察器去监听当前元素是否出现在页面

function callback(data) { 
    navigator.sendBeacon(url, { target: data[0].target, text: '内容可见' }) 
} 
// 交叉观察器配置项 
let options = {}; 
// 生成交叉观察器 
const observer = new IntersectionObserver(callback); 
// 获取目标节点 
let target = document.getElementById("target"); 
// 监听目标元素 
observer.observe(target);

三.后续

开发的时候可以封装这三种上报方法

// sendBeacon 上报
export async function sendBeacon( {url = '', params }: reportParams) {
    if (navigator?.sendBeacon && url) {
      const isSuccess = await navigator?.sendBeacon(url, JSON.stringify(params));
      if (isSuccess) return true;
    }
    return false;
}

// img 上报
export function sendImg({ img = '', params }: reportParams) {
    return new Promise<boolean>((resolve, reject) => {
        const imageData  = objectToQueryString(params)
        const img_o = new Image();
        img_o.onload = () => resolve(true);
        img_o.onerror = () => reject(false);
        img_o.src = `${img}?${imageData}`;
    })
}

// ajax 上报
export function sendAjax({ req = '', params }: reportParams) {
    return new Promise<boolean>((resolve, reject) => {
        if (req) {
            postAction(req, params)
                .then(() => resolve(true))
                .catch(() => reject(false));
        } else {
            reject(false);
        }
    });
}

使用的时候再导出一个真实上报函数,有由用户决定使用什么上报组合

// 基础上报函数
export async function reportEvent(params: reportParams, reportType:string[] = [IMG, BEACON, AJAX]) {
    let finalType = false
    for (const key in reportType) {
        if (!finalType) {
            try {
                await EVENT_REPORT_FUNCTION_MAP[key](params).then(()=>{
                    finalType = true
                })
            } catch (error) {
                console.error(error)
            }
        }
    }
    return finalType
}
// 基础上报函数
export async function reportEvent(params: reportParams, reportType:string[] = [IMG, BEACON, AJAX]) {
    let finalType = false
    for (const key in reportType) {
        if (!finalType) {
            try {
                await EVENT_REPORT_FUNCTION_MAP[key](params).then(()=>{
                    finalType = true
                })
            } catch (error) {
                console.error(error)
            }
        }
    }
    return finalType
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 前端埋点是指在网页或者移动应用中收集用户行为数据的技术,常用于统计用户操作情况、错误信息等。 实现方法包括: 1. 使用 JavaScript 代码在页面中添加事件监听器,当用户进行特定的行为时,通过代码上报数据。 2. 使用第三方埋点工具,如百度统计、Google Analytics等,来实现埋点前端分析,是指对前端采集的数据进行分析,以深入了解用户行为和网站性能。 实现方法包括: 1. 导出前端数据到数据分析工具,如数据仓库、数据可视化工具等,进行数据分析。 2. 使用前端分析工具,如百度统计分析、Google Analytics分析等,直接对数据进行分析。 ### 回答2: 前端埋点是指在前端代码中插入代码片段,用于收集用户行为数据和业务指标数据。前端埋点实现主要包括以下几个步骤: 1. 确定埋点数据:根据业务需求,确定需要收集的用户行为以及相关的业务指标数据。 2. 插入埋点代码:在前端代码的合适位置,插入埋点代码片段。埋点代码可以使用JavaScript或者其他相关技术实现,根据需要来收集特定的数据。 3. 参数传递和组织:根据具体业务场景,将收集到的数据以参数的形式传递给后端或者其他数据处理平台。可以使用JSON等格式来组织数据,便于后续的分析和处理。 4. 数据上报和存储:根据业务需求和技术实现选择合适的方式将埋点数据进行上报和存储。可以使用AJAX请求、图片请求、WebSocket等方式将数据发送给后端服务器或者第三方数据服务平台进行存储和分析。 分割是指将较大的前端代码资源进行拆分,提升页面加载速度和用户体验。分割的实现包括以下几个步骤: 1. 代码拆分:根据页面的功能和需要加载的资源,将前端代码进行拆分。可以根据模块、组件、页面等维度进行拆分,将不同模块的代码分别打包为不同的文件。 2. 预加载和按需加载:根据页面的情况,可以选择将一些重要的代码资源进行预加载,尽快提供给用户。同时也可以根据用户的具体操作情况,按需加载页面中的某些功能或者数据。可以借助Webpack等工具实现代码的预加载和按需加载。 3. 异步加载和懒加载:对于一些较大的代码资源,可以使用异步加载的方式进行加载,提高页面的加载速度。同时,也可以使用懒加载的方式,将页面中一些不需要立即展示的部分进行延迟加载,减少初始加载时的资源开销。 4. 缓存和优化:在代码分割的同时,可以对分割出来的代码进行缓存和优化,提升页面的加载速度和用户体验。可以使用浏览器的缓存机制,合理利用缓存和压缩等技术手段,减少请求的次数和资源的大小。 总之,前端埋点和分割是常用的前端技术手段,可以帮助收集用户数据和提升页面加载性能。根据具体业务需求和技术实现选择合适的方式进行实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值