最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 自动埋点核心原理揭秘

    正文概述 掘金(大转转FE)   2021-03-05   633

    前言

    项目上线几天后,产品同学突然向你要一个需求文档内没有,但很重要的埋点,怎么办?是不是又到了甩锅的时间了? 为了避免这种情况,且鉴于我司"没有落地的决策是垃圾"的精神,我们产出了这个自动埋点工具。

    调研

    现在业界有很多数据采集的厂商,像 Heap、GrowingIo、神策等。 也都提出了可视化埋点、无埋点等方案,其中可视化埋点是需要圈选的,并不能满足我们的需求。 而无埋点方案也分很多种,一种是保存所有用户点击的 xpath,然后交由后端进行分析,另一种是分析所有标签,收集所有的控件,当用户点击到控件后进行发送。 这两种方案虽然能解决我们一部分问题,但是功能还远远不够,暴露的问题也很多,像:

    1. 不能灵活的自定义属性
    2. 传输时效差
    3. 数据可靠性欠佳
    4. 服务器和网络传输压力更大 等问题

    至此业界暂时没有一个完美的方案解决这个问题。 所以我们抛弃了上述所有方案,决定实现一套更优的方案-自动埋点。

    以往大家思想都是从页面结构出发,而我们是直接基于事件的角度来实现的。 下面就来聊一聊我们实现的核心方法

    关键技术

    1. 丰富的配置项
    2. 事件捕获
    3. 埋点发送

    配置项

    一个功能强大的库,少不了丰富的配置功能。 我们的配置项主要分为使用范围、自定义上报事件、路由配置、页面类型等等。 其中使用范围,我们控制了发送埋点是项目级的还是页面级的。 自定义上报事件是用来区分具体上报曝光还是点击或者其他埋点的。 路由配置支持 hash 和 history 类型。 页面类型主要是用来控制发送参数时,参数组合格式配置等等。 具体实现暂不展开说明,也非本文重点,接下来我们说一下这个方案的核心模块

    事件捕获

    首先我们知道,项目中埋点上报大体分为:

    1. 曝光
    2. 点击
    3. 模块曝光
    4. 关闭 ...

    那我们完全可以捕捉到这些事件,并触发埋点就可以达到我们的目的了,接下来我们逐个分析。

    曝光埋点

    曝光埋点我将其分为三种:

    1. 直接进入全新页面
    2. SPA 项目的页面跳转后的页面
    3. 当前页面隐藏后再唤出的情况

    第一种的解决方案: 前期实现,认为加载了这个 js,就算是曝光了,但是后期因为业务复杂度问题,用户状态繁多,我们将其放置到首个接口请求后进行发送,这样就可以让统计更精细化,也更符合 PM 的预期。

    第二种的解决方案: hash 路由:我们可以通过监听hashchange解决 history 路由:我们对pushStatereplaceState等源生事件进行了重写,直接上代码

    let that = this;
    let reWrite = function (type) {
      let real = history[type];
      return function () {
        let realFun = real.apply(this, arguments);
        let newEvent = new Event(type + 'AutoLego');
        newEvent.arguments = arguments;
        window.dispatchEvent(newEvent);
        return realFun;
      };
    };
    history.pushState = reWrite('pushState');
    history.replaceState = reWrite('replaceState');
    window.addEventListener('replaceStateAutoLego', function (e) {
      that.skipRouterCommon();
    });
    window.addEventListener('pushStateAutoLego', function (e) {
      that.skipRouterCommon();
    });
    

    在执行源生的pushStatereplaceState之后调用我们自定义事件pushStateAutoLegoreplaceStateAutoLego,来达到发送埋点的目的

    第三种的解决方案: 这个比较简单,就是监听visibilitychange事件,来发送埋点

    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'visible') {
        that.showLego();
      }
    });
    

    点击埋点

    为了更加精准的发送埋点,满足实时性、自定义属性、可靠性等特点,这边采用重写addEventListener方法并配合当前点击 DOM 节点的属性来进行实现,,部分代码逻辑

    // 扩展监听事件
    Element.prototype.realAddEventListener = Element.prototype.addEventListener;
    Element.prototype.addEventListener = function (a, b, c) {
      if (a === 'click') {
        this.realAddEventListener(a, reWriteClick(b), c);
      } else {
        this.realAddEventListener(a, b, c);
      }
    };
    // 扩展移出监听事件
    Element.prototype.realRemoveEventListener =
      Element.prototype.removeEventListener;
    Element.prototype.removeEventListener = function (a, b, c) {
      if (a === 'click') {
        this.realRemoveEventListener(a, reWriteClick(b), c);
      } else {
        this.realRemoveEventListener(a, b, c);
      }
    };
    

    其中只针对click事件进行了重写,在执行真正的点击回调函数b之前,进行数据处理并发送埋点。 这样就实现了针对真实的点击事件进行捕捉,避免了收集大量的无效点击和自定义属性等问题,也不用做定时发送这样的逻辑来影响性能了。

    模块曝光

    模块曝光顾名思义,就是只某个模块展现,就会发送一个展现埋点。 通常应用在不同状态展示不同模块的情况。 遗憾的是,因为技术有限,我还无法将其完全自动化,只能做到半自动化(⊙︿⊙),如果大家有更好的方法,希望大家不吝赐教。 下边说一下,我实现的具体方法。 我针对要曝光的模块,在 dom 上做了一个标记autolego-,当 dom 发生变化时,匹配到这个标记的时候,发送埋点,具体实现如下:

    // 代码略有删减
    let that = this;
    var MutationObserver = window.MutationObserver;
    function dfs(item) {
      if (item.id.indexOf('autolego-') > -1) {
        that.showElLego(item);
      }
      if (item.childNodes) {
        item.childNodes.forEach((childItem) => {
          dfs(childItem);
        });
      }
    }
    var observer = new MutationObserver(function (mutations, observer) {
      mutations.forEach((item) => {
        dfs(item.target);
      });
    });
    observer.observe(document.body, {
      subtree: true,
      childList: true,
      attributes: true,
    });
    

    从代码中,大家可以发现,核心点只有两个,其中一个是利用了MutationObserver方法,另一个是对 DOM 树进行了深度遍历。

    关闭

    因为浏览器的兼容问题的存在,关闭事件这边分别采用onpagehideonbeforeunload对 IOS 和其他系统来区分实现

    if (!!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) {
      window.onpagehide = () => {
        this.unLoadLego();
      };
    } else {
      window.onbeforeunload = () => {
        this.unLoadLego();
      };
    }
    

    事件捕获我们暂且说到这里,延伸这个思路,我们还可以做的更多,数据更加全面准确。

    发送

    在发送埋点这里大家需要注意一下,像关闭埋点,如果页面关闭过快,我们的埋点会被 abort 掉,所以这里推荐大家用 ajax 来实现, 如果对兼容性要求不高,也可以用navigator.sendBeacon来实现,这个方法主要是为了满足统计和诊断代码而生的,也希望以后兼容性能越来越好。

    if (navigator.sendBeacon) {
      var fd = new FormData();
      fd.append('legoType', 'autoLego');
      navigator.sendBeacon(`https://${urlPath}`, fd);
    }
    

    效果

    自动埋点核心原理揭秘

    如图可见,当点击 banner 的时候,自动发送了点击埋点,同时触发了关闭页面的埋点,以及进入到下个页面的曝光埋点。 与此同时,在 backup 区域,也带上了更多信息,数据更加的全面精准。 备注:效果是通过我们的可视化埋点平台演示的,后期有机会我们也会介绍,敬请期待

    总结

    通过上面的介绍,相信大家已经对 自动埋点 的核心技术已经很清楚了。

    其实就是针对源生方法的重写来实现同步发送埋点的效果。

    相较于市面上的无埋点方案,自动埋点的可控性,可扩展性也是非常有优势的。

    关于自动埋点暂时只写到这里啦,虽然有些功能还有欠缺,但我相信,不断迭代扩展是肯定可以替代掉手动埋点的。不对的地方也希望大家多多指正,一起进步~

    每天进步一点点,质变从关注 大转转 FE 开始。


    起源地下载网 » 自动埋点核心原理揭秘

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元