开发一套埋点方案

开发一套埋点方案

Tags
前端
开发记录
Published
August 2, 2020
Author
LIAOKUN

概述

出了个需求需要交付一套独立的页面代码出去,埋点方案也不能沿用现成的代码,因此需要从头开发一套埋点方案。当然可以把现有的埋点代码复制过来使用,考虑到之前也没有接触过这块的内容干脆简单学习一下,整理出这份文档记录。

使用埋点的原因/埋点类型/埋点方案。。。

这里就不赘述了,有很多文章介绍这块的内容。可以见参考文章: https://juejin.im/post/6844903650603565063
 

本次项目中使用的埋点类型/埋点方案

上文的参考文章中有提到:
前端监控可以分为三类:数据监控、性能监控和异常监控。
本次添加的埋点属于数据监控,是为了记录页面数据和用户操作数据,比如页面的访问量/曝光量/到达率/页面中元素产生的交互(点击)次数等。
而对于埋点方案来说,前端的埋点一般会分为
  • 代码埋点
代码埋点是最常用的,开发人员可以在触发的事件或交互中添加代码将埋点请求发送出去
  • 可视化埋点
这个方案几乎没有接触过,看描述应该是通过 建站系统 这类的后台为运营等非开发岗位使用者提供插入代码埋点的能力。对比我们现有的几个建站系统中也没有使用到该方案。
  • 无埋点
无埋点在现有的埋点代码中也有比较多的使用,最常见的便是监听document的 click 事件,在click的交互中向服务端发送埋点请求。
本次项目中使用的是代码埋点+无埋点的方案。无埋点可以极大地节约代码量并提升埋点的维护性,只需要为页面元素添加特定的属性,比如是否需要发送埋点请求,埋点请求数据等,就可以完成数据埋点;而还要额外加上代码埋点的原因是监听交互事件不能满足全部的业务需求,比如我们还希望统计到页面的访问、曝光情况,或是希望在页面的复制事件(实现复制大部分都是用的clipboard.js吧)成功/失败之后也发送请求告知服务端,这些场景下无埋点的方案并不能很好地应付。

实现流程

正如埋点方案中提到的,代码实现分为两部分:无埋点(事件监听)与代码埋点。 首先封装埋点请求发送的方法,以图片的方式将请求发送出去。这里有踩到一个坑:最开始为了避免内存爆炸使用了同一个Image对象发送请求,但是在弱网+高频发送请求的情况下,基于同一张图片发出的多个请求会被浏览器取消只保留最后一个。举个例子: 假设弱网下一个图片请求被发送出去并完成请求需要300ms,创建一个Image标签 let logImage = new Image(); 每隔100ms修改 logImagesrc 属性发出三个埋点请求,会发现前面两个请求被浏览器 cancel 了,只有最后一个请求成功发出并完成。因此使用如下方式发出请求:
// 发送埋点请求 sendTrackLog(trackLog = []) { let trackImg = new Image(); trackImg.src = `${host}/log?${'埋点数据'}`; trackImg.onload = () => { trackImg = null; } }
暴露 sendTrackLog 即可完成代码埋点并提供给无埋点使用。 接下来实现无埋点:
/** * @note 点击埋点 * @param track-click-log 单次点击 * @param {Array} trackLog [location,questionId] */ clickLog() { // 点击 document.addEventListener("click", function(event) { // 阻止事件冒泡 event.cancelBubble = true; event.stopPropagation(); let target = event.target; let trackLog = (target && target.getAttribute("track-log")) || (target && target.getAttribute("track-click-log")); // 如果dom冒泡了,当前target找不到track-log 需要向上去找,使用closet向上dom查找track-log if (!trackLog) { // 重新找到新的target && trackLog重新赋值 target = this.closest(target, "track-log") || this.closest(target, "track-click-log"); if (!target) return; trackLog = target.getAttribute("track-log") || target.getAttribute("track-click-log"); } clearTimeout(trackTimeout); trackTimeout = setTimeout(() => { this.sendTrackLog(JSON.parse(trackLog)); }, 150); }); } closest(el, selector) { if (!el) return; var matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; while (el) { if (matchesSelector.call(el, selector)) { break; } el = el.parentElement; if ( (el && el.getAttribute("track-log")) || (el && el.getAttribute("track-click-log")) ) { break; } } return el; }
别的交互事件实现方式类似,使用的时候为需要添加点击埋点的元素添加 track-click-log 属性即可。