聊聊令人头疼的埋点

3,661 阅读10分钟

埋点定义

埋点,是指在应用中添加代码,以收集用户的操作行为和数据,以便后续进行数据分析和产品决策。这些代码通常被称为埋点代码,它们将事件(如点击、滚动、搜索等)和属性(如时间、位置、设备等)捕捉并发送到数据平台。通常情况下,这些数据用于分析用户行为、监控应用程序性能、改进产品功能等方面。

转转 H5 采用的是手动埋点方式,App 内的页面通常需要添加各种埋点,以验证和辅助产品后续决策。今天就和大家聊聊令笔者头疼的埋点,也希望能加深您对埋点的理解~

以下部分内容、代码,来源于 chatGPT,如有错误,欢迎指出~

埋点内容

首先埋点内容一般会包含用户信息、页面信息、事件信息、访问信息等。

  • 用户信息:包括用户的唯一标识(uid)、设备标识(token)、访问设备、浏览器版本以及网络状态等
  • 页面信息:包括当前页面的 URL、标题、页面 ID 等信息
  • 事件信息:包括用户的行为事件,如点击、滚动、鼠标移动等,以及事件的时间戳、元素路径、事件类型等信息
  • 访问信息:包括用户来源、搜索关键词、渠道信息等,计算整个链路的渗透,或者在出现问题时,帮助还原整个用户操作路径,帮助开发者更快的定位、修复问题。

埋点方式

常见的埋点方式大体可以分为手动埋点、可视化埋点和全埋点三种。

  • 手动埋点

    在代码中手动加入埋点,相应事件触发的时候,再上报相关埋点。当需要精细化数据或者希望根据业务诉求,定制化添加埋点的时候,就很适合使用这种方式。但缺点就是额外工作量很大,也需要相关的 QA 介入测试,一些复杂的埋点很容易出错,导致延误需求数据分析的时间

  • 可视化埋点 需要页面/项目预先接入可视化埋点 SDK,并开启可视化埋点开关,然后相关人员登录可视化圈选后台,选择相应的页面以及圈选需要上报的相关行为埋点,圈选平台和 SDK 进行通信,让 SDK 拿到需要上报的埋点,然后 SDK 自动上报相关的埋点。

  • 全埋点

    是一种将应用程序中所有用户行为都收集和分析的埋点技术,例如打开页面、切入后台、点击某个区域、某个区域曝光等等,优点就是可以更全面、更细致地了解用户行为和需求,缺点就是由于自动记录了各种操作行为的数据,会导致大量的无意义的行为被上报,对服务端的压力比较大,并且也考验从纷繁复杂的埋点中找到所需埋点的能力

埋点流程

截屏2023-04-09 21.53.15.png

埋点流程大体可以分为埋点触发、上报、校验以及上报到数据平台后的埋点清洗、过滤和分析,进而产出下一步决策。

  1. 埋点触发

    埋点触发大致分为自动触发和手动触发两种方式,上面提及的页面展现通常就是自动触发,当页面打开的时候,就自动上报了。但是像点击埋点就可以用手动触发的,只有当区域被真正点击时,才会进行上报。

  2. 埋点上报

    其中埋点上报又分为立即上报和延迟上报两种。立即上报的逻辑相对简单,在埋点事件触发时,就立即上报。但是缺点也很明显,就是上报的埋点量巨大,会给埋点服务造成巨大负担。延迟上报,就是将一段时间内的埋点,收集起来,然后一次性上报。这样无疑就会使上报的次数,急剧减少,减轻了埋点服务压力。但是其中又会涉及埋点上报去重、埋点触发时间校准(如果客户端时间不准怎么办?)等等其他问题,因此相对立即上报来说,延迟上报逻辑上要复杂一些。并且需要数据层面进行过滤、清洗。

  3. 埋点校验

    开发者手动添加了部分埋点,需求上线前需要进行验证,确保按照要求进行了上报,其中校验可以使用人工触发,抓包进行校验。也可以通过编写自动化脚本,模拟使用,进行校验。转转侧使用相关的后台,可以通过筛选相关用户、来源以及不同环境,实时接收相关的埋点,进行校验。

  4. 埋点分析

    埋点上报之后,数据平台就会拿到相关的埋点数据,对纷繁复杂的数据,进行过滤、清洗,得到产品需要的数据,然后产品就会对数据进行分析,有时可以发现一些问题,以及对后续决策产生影响。

埋点常见类型

埋点的触发通常与埋点的类型相关,接下来列举几种常见的埋点类型:

  • 页面展现

    在页面展现时进行上报,H5 环境下一般通过监听 onshow 或者 visibilitychange 事件来实现,但是这两者都有一定的兼容性问题。

    而如果是处于 hybrid 环境,则可以利用宿主环境(客户端)暴露的生命周期来实现,借用原生的生命周期来实现,也更加准确些。页面展现一般用来记录页面的 PV/UV,算是一项非常基础的数据了。

  • 点击

    用户点击某个区域时上报,可以上报相关业务参数,也可以包含点击位置信息,其中位置信息可以用来生成热力图,确定页面的热区,从而可以知道用户对哪部分更加感兴趣,哪部分的转化效率更高,以便调整后续的产品策略。H5 中一般可以通过事件委托来实现,在根结点监听点击事件,当事件冒泡到根结点阶段,触发相应事件。

     document.addEventListener('click', function(e) {
       const target = e.target
       // do something
     }, true);
    
  • 区域曝光

    当某个区域出现在视口内,一定时间内进行上报,一般配合点击、下单等数据,观察整个路径的漏斗转化。由于会涉及重复上报的问题,所以一般区域都会有一套规则,生成该区域的唯一标识,防止重复上报。以及在商品列表的场景中存在翻页的情况,就需要再使用 MutationObserver 监听 DOM 的变化,动态的调用 IntersectionObserver 进行重复监听。

    H5 一般有监听页面滚动事件和使用 IntersectionObserver 两种方式来实现

    // 1. 监听页面滚动实现
    const element = document.querySelector('.exposure-ele');
    ​
    window.addEventListener('scroll', () => {
      const elementPosition = element.getBoundingClientRect();
      const windowPosition = {
        top: 0,
        left: 0,
        bottom: window.innerHeight,
        right: window.innerWidth
      };
    ​
      if (isElementInViewport(elementPosition, windowPosition)) {
        console.log('Element is in viewport!');
      } else {
        console.log('Element is not in viewport!');
      }
    });
    ​
    function isElementInViewport(elementPosition, windowPosition) {
      return (
        elementPosition.bottom > windowPosition.top &&
        elementPosition.top < windowPosition.bottom &&
        elementPosition.right > windowPosition.left &&
        elementPosition.left < windowPosition.right
      );
    }
    ​
    // 2. 使用 IntersectionObserver 实现
    const element = document.querySelector('.exposure-ele');
    ​
    const options = {
      root: null,
      rootMargin: '0px', // 设置视口四边延伸的范围,可以利用此做列表数据的提前加载
      threshold: 0.5     // 区域与视口相交的阈值
    };
    ​
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          console.log('Element has entered the viewport!');
        } else {
          console.log('Element has left the viewport!');
        }
      });
    }, options);
    ​
    observer.observe(element);
    ​
    
  • 页面停留时长

    定义用户从进入页面到离开页面的时长,一般需要精确到毫秒级别。停留时间越长,一般代表用户对当前页面越感兴趣,预示产品决策是否可以对此进行一些深耕。

  • 热力图

    通过颜色深浅,标识用户对页面各区域点击的频率。颜色越深,代表点击频率越高,是一种直观、高效发现吸引用户区域的方式。比如常用于商场首页金刚位,可以清晰发现用户对各品类的喜好,就可以动态调整金刚位的类目。一般是通过统计点击埋点上报的位置,进行实现。

    其中位置又可以分为绝对位置和区域位置两种。绝对位置是指点击时的 x、y 坐标,但是由于各个手机的分辨率不同,点击同一区域的 x、y 坐标也不一样,就需要进行多分辨率的调整、整合,比较复杂。

    转转现在采用的是后者区域位置,通过页面ID(pageId)、区域ID(sectionId)以及区域次序ID(sortId),对一个区域进行定位,当点击时会上报相关的 pageId、sectionId 以及 sortId,然后就可以统计出页面某个区域、某个次序的点击率,生成相应热力图了。 以下是转转游戏账号首页的热力示意图:

截屏2023-04-09 15.11.10.png

  • 性能埋点

    通过记录页面加载过程不同阶段的耗时,帮助开发者发现性能问题,提升页面加载速度,发现可优化的点,提升用户体验。甚至可以与白屏检测相结合,在页面出现问题时,及时报警通知相关人员查看、处理。

埋点发送方式

埋点发送即将埋点相关数据发送给数据平台,一般有接口方式、img 标签方式和 sendBeacon 三种方式。

  • 接口方式:

    通过接口的形式将埋点的信息进行上报,兼容性比较好,但是一些网站可能会禁用脚本,导致失效。

  • 创建 img 标签:

    很多公司都采用 img 标签携带埋点信息进行上报,一方面是图片请求不存在跨域限制(一般而言,埋点发送域名都不是当前域名),另一方面图片标签不需要真正插入到 DOM 节点中,只需要实例化 Image,设置 src 属性就会发出请求,不会阻塞页面渲染,对性能影响较小。

    const img = new Image()
    img.src = 'https://example.com/log?xxx'
    

    为了追求埋点请求尽可能小,大多采用的是 1*1 像素的透明 GIF 来上报,因为在各种图片格式下,这种相对较小。

  • sendBeacon 发送

    该 api 是专门被设计来满足统计和诊断代码的需要,通常需要在页面卸载之前,将相关埋点发出。过早的发送数据可能导致错过收集数据的时机,因此需要等到页面即将卸载时发送数据。在 sendBeacon 出现之前,很难保证在页面卸载之前,可以将数据成功发送,因为用户代理通常会忽略在 unload 事件处理器中产生的异步 XHR。

    过去为了解决这个问题,开发者们想出了一些 hack 的方法:

    1. 发起一个同步 XMLHttpRequest 来发送数据
    2. 创建一个 img 元素并设置 src,大部分用户代理会延迟卸载(unload)文档以加载图像
    3. 创建一个几秒的 noop 循环

    但是无独有偶,上述方法都存在一个问题,那就是会延迟当前页面的卸载,导致下一个页面出现的更晚。

    而 sendBeacon 不存在上述问题,它数据发送是可靠的、是异步的,正由于异步发送数据,所以不影响下一导航的载入。一般可以监听 visibilitychange 的 hidden 状态来发送埋点

    document.addEventListener('visibilitychange', function logData() {
      if (document.visibilityState === 'hidden') {
        navigator.sendBeacon('/log', analyticsData)
      }
    })
    

总结

以上从埋点内容、方式、流程、常见埋点的类型以及发送方式等方面,介绍了埋点相关的基础概念以及转转采取的方案,希望能对您有所帮助~

参考及引用