数据埋点:声明式埋点(无痕埋点)

声明式埋点

  • 预先声明埋点类型(提前封装好自定义的指令),在 DOM 或者组件上,通过设置即可以完成事件的统一上报;
    • 声明式埋点是基于 vue 框架的自定义指令,可以一定程度的减小代码冗余解决代码耦合的问题。
    • vue 中提供了自定义指令,在模版元素中插入自定义指令,可以跟踪元素的结构变化、跟踪数据变化,比 DOM 的 data 属性,从而更加方便抓取跟踪;
  • 理论上,声明式埋点只需要关注两个问题:
    • 需要埋点的 DOM 节点;
    • 所需携带的数据;
  • 声明式埋点标准写法:
    // key表示埋点的唯一标识;act表示埋点方式
    <button data-stat="{key:'111', act: 'click'}">埋点</button>
    
  • 埋点触发及数据回传:
    • 声明埋点后、遍历 DOM 树,找到 [data-stat] 节点,给这个 button 绑上 click 事件;
    • 当事件被触发后,把参数 111 在回调函数中通过请求发出去。

声明式埋点优缺点

  • 声明式埋点优点:
    • 埋点和业务代码实现了解耦;

      声明埋点是在 DOM 节点(html)上,而业务逻辑通常在 Javascript 文件中;

    • 提高代码可维护和可阅读性;
    • VUE 比较流行的埋点方式;
  • 声明式埋点缺点:
    • 需要手动在需要埋点的节点中添加指令。

声明式埋点需要解决的问题

  • 遍历 DOM 树的时机
    • 例如:一个表格的行数据是通过异步加载,而表格行中的操作按钮需要埋点,那么在 DOM ready 的时候去遍历,显然是无法找到的;
  • 绑定埋点事件次数的问题
    • 怎样保证埋点事件不会被重复绑定到元素上,一次操作发了N个埋点请求?
  • 如何处理特有的埋点行为,
    • 例如:页面展现埋点,区域展现埋点?
  • 如何在解绑时,销毁已绑定的事件?

声明式埋点方案核心内容

  • 通过声明式埋点来解耦业务代码
  • 埋点方案需要兼容 Vue 应用和 jquery 应用(甚至所有应用)
  • 需要支持页面展现埋点、区域展现埋点、点击埋点等多种埋点方式
  • 极端情况下需要支持命令式埋点

钩子函数

  • 按照官方文档,一个指令定义对象提供如下几个钩子函数:

    • bind
      • 只调用一次,指令第一次绑定到元素时调用,在这里可以进行一次性的初始化设置。
    • inserted
      • 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
    • update
      • 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
        • 指令的值可能发生了改变,也可能没有。
      • 但是可以通过比较更新前后的值来忽略不必要的模板更新。
    • componentUpdated
      • 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    • unbind
      • 只调用一次,指令与元素解绑时调用。
    • 其中 bind 和 update 是比较重要的两个钩子函数,
      • 它能跟踪 DOM 的出现和变化,获取数据并数据上报埋点;
  • 指令钩子函数会被传入以下参数:

    • el
      • 指令所绑定的元素,可以用来直接操作 DOM。
    • binding
      • 一个对象,包含指令的指令名、绑定值、表达式等。

具体实现

  • 页面曝光
    • 对于 vue 的单页应用来说,页面的切换跟踪就是路由的变化;
    • 但是在统一的路由钩子、放置每个页面不同的数据不太现实,于是就考虑在每个 view 页面根部埋下页面曝光数据,只要用户一访问该页面,就触发页面曝光;
  • 坑位曝光
    • 坑位的曝光实际就是一个 DOM 出现在用户视野范围内进行抓取;
    • 需要借助全局滚动(scroll)事件,使用节流的方式检测目标坑位是否在可视范围内,在的话就触发坑位曝光
  • 坑位点击
    • 坑位的点击需要对目标坑位进行点击事件绑定;
    • 在不影响目标坑位原有事件基础上,绑定点击的埋点上报,在元素unbind的时候解除对应点击事件

页面曝光(v-page-exposure)

  • v-page-exposure
      <div class="root" v-page-exposure="'a.b.c.d'"></div>
    
    • a.b.c.d 分别为:应用代码.页面代码.模块代码.坑位代码,就是埋点统计需要的代码信息,把信息收集上报即可。
      • 需要注意的是指令信息是一个 js 表达式,如果不加引号会默认执行 js 变成一个对象取值报错。

坑位曝光(v-exposure)

  • v-exposure
      <div v-exposure="{gcms:[{type:1, uri:22},{type:2, uri:41}],seq:[1],spm:'a.b.c.d'}"></div>
      复制代码
    
    • 坑位曝光收集的是一个 js 对象,包含一些坑位的代码信息、位置信息、商品信息、跳转信息等。
    • 坑位曝光采用实时上报,且曝光一次就不再曝光,
      • 所以每次 bind 的时候、把所有曝光坑位收集在一个数组,坑位出现在用户可视范围内一次就上报弹出数组。

坑位点击(v-bury-click)

  • v-bury-click
      <div v-bury-click="JSON.stringify({gcms:[{type:1, uri:22},{type:2, uri:41}],spm:'1.0.0.0'})"></div>
      复制代码
    
    • 坑位点击也是一个 js 对象,包含坑位的各种信息,在页面加载的时候绑定点击事件,用户点击的时候上报埋点。
    • 在实际使用过程中,埋点需要跟踪最新的数据信息,比如:
      • 在加入购物车的环节中,加入购物车的按钮是同一个不变,但下一次选择商品的信息可能发生改变,所以每次点击按钮需要传递最新的埋点信息。
    • 由于指令信息是一个 js 表达式,对象是一个引用类型,改变对象的值不会触发 update 钩子函数,所以需要 JSON 字符串化一下。