Skip to content

为了灵活度和方便,一般项目结合自动埋点手动埋点

手动埋点

手动埋点的重点应该在于 声明式 api形式调用, 处理好参数处理和上送即可

而在 Vue 里则可以考虑自定义指令来封装

js
/**
 * 发送请求,使用image标签跨域
 * @param url 接口地址
 */
function sendRequest(url) {
    if (page.length === 0) {
        console.error('请配置有效的page参数', '@burying-point');
        return;
    }
    var img = new Image();
    img.src = url;
}

自动埋点

🤔 在 vue3 里用 use 的形式基于手动埋点封装通用级别的埋点也很容易 = = 那还需要自动埋点全收集吗

  • 页面切换时,进行采集,即 url 变化时触发的事件;
  • 页面失去焦点,得到焦点时,进行采集。即 focus,blur 事件;
  • 页面通过浏览器 tab 切换离开,切换回来时,进行采集,即 visibilitychange 事件;

因为单页多页的页面生命周期不同,实现访问量的自动埋点的设计方向就是 url 的变化(单页多页都会变化)

  1. url变化 = window.location.origin + window.location.pathname + window.location.hash 这三部分的任一部分变化,即为 url 变化,并不包括 window.location.search 这部分的变化;
  2. 在 SPA 中,如果一个页面内有多个 tab(嵌套子路由),当切换 tab 时,开发者也改变他的 url 的 window.location.pathname,此时也会认为是页面切换,也会产生上报数据(希望的效果是切换页面内的Tab不属于访问量的埋点)

👇 history apipushstate,replacestate 自定义事件,因为 BOM 并没有提供相关的 api 支持 EventListener(传递回调来支持拓展), 需要自行封装使用

js
/**
 * 拼接通用化上报参数
 * @param {string} 重写路由事件类型
 */
function resetHistoryFun(type){
  // 将原先的方法复制出来
  let originMethod = window.history[type]
  // 当window.history[type]函数被执行时,这个return出来的函数就会被执行
  return function(){
    // 执行原先的方法
    let rs = originMethod.apply(this, arguments)
    // 然后自定义事件
    let e = new Event(type.toLocaleLowerCase())
    // 将原先函数的参数绑定到自定义的事件上去,原先的是没有的
    e.arguments = arguments
    // 然后用window.dispatchEvent()主动触发
    window.dispatchEvent(e)
    return rs;
  }
}
window.history.pushState = resetHistoryFun('pushState') // 覆盖原来的pushState方法
window.history.replaceState = resetHistoryFun('replaceState') // 覆盖原来的replaceState方法

window.addEventListener('pushstate', reportBothEvent)
window.addEventListener('replacestate', reportBothEvent)

👆 和微前端重写router的思路一样.... 多页呢?url上参数部分呢? 嵌套子路由呢?

js
window.addEventListener('focus', ()=>{
  console.log('页面得到焦点')
});

window.addEventListener('blur', ()=>{
  console.log('页面失去焦点')
})
js
document.addEventListener('visibilitychange',  () => {
  if(document.hidden) {
    console.log('页面离开')
  } else {
    console.log('页面进入')
  }
})

数据上报方式是 XMLHttpRequest window.navigator.sendBeacon 基于 h5sdk 上报逻辑架构。

埋点

目前接入lego的埋点方案

TODO:

关键点:

如何获取元素的唯一标记,并可通过该标记反向选择元素 如何随着鼠标的移动,给元素添加浮层 频繁的跨域 iframe 通信,该如何简化 元素的曝光和点击如何同元素标记匹配上报

XPath

js

// 其中 findIndex 代码如下:
function findIndex(ele, currentTag) {
    let nth = 0;
    while (ele) {
        if (ele.nodeName.toLowerCase() === currentTag) nth += 1;
        ele = ele.previousElementSibling;
    }
    return nth;
}

function getXpath(ele) {
    let cur = ele;
    const path = [];
	while (cur.nodeType === Node.ELEMENT_NODE) {
		const currentTag = cur.nodeName.toLowerCase();
		const nth = findIndex(cur, currentTag);
		path.push(`${(cur.tagName).toLowerCase()}${(nth === 1 ? '' : `[${nth}]`)}`);
		cur = cur.parentNode;
	}
	return `/${path.reverse().join('/')}`;
}

PC 后台埋点

采集到这种 xPath /html/body/div[16]

👆 因为弹窗 div在最外层,顺序随机,这些自动埋点(基于位置)都是无效的

👆 同理 顶部Tab 页签顺序随机,左侧菜单与用户权限有关,顺序随机

此类埋点:

  1. 需要手动通过标签属性埋点,可以考虑全局自定义ant组件标签属性
  2. 自动埋点尝试不上送,即有自定义手动埋点属性时不上送 xpath

前置知识-无痕/可视化埋点,xpath

js
function mouse_down(event) {
  var x = event.clientX,
    y = event.clientY;

  // 根据坐标获取DOM元素
  var element = document.elementFromPoint(x, y);
  if (!element) {
    console.log("error: no element");
  }
  // 根据DOM元素获取xpath
  readXPath(element)
}

xpath: /html/body/div[2]/div[1]/div[1]/span[1]/input[1]/

js
function readXPath(element) {
  count = 1;
  result = "";

  while (true) {
    count += 1;
    if (count > 99) {
      break;
    }
    if (element == document.body) {
      console.log("/html/body/" + result);
      break;
    } else {
      tag_index = 0;
      tmp = element.parentElement;
      for (var i = 0; i < tmp.childElementCount; i++) {
        if (tmp.children[i].tagName == element.tagName) {
          tag_index += 1;
        }

        if (element == tmp.children[i]) {
          result =`${element.tagName.toLowerCase()}[${String(tag_index)}]/${result}`
          break;
        }
      }

      element = tmp;
    }
  }
}

document.onmousedown = mouse_down;

失去埋点数据的可读性

以及布局调整后,埋点数据重新计算,无法整合

https://segmentfault.com/q/1010000043016040

https://developer.mozilla.org/zh-CN/docs/Web/XPath/Introduction_to_using_XPath_in_JavaScript

https://zhuanlan.zhihu.com/p/313016178

https://juejin.cn/post/7185362356652736570

https://juejin.cn/post/7156070053635424264

https://www.ucloud.cn/yun/82546.html