前端代码埋点实现

Huy大约 4 分钟javascriptjavascript

前端代码埋点实现

1. 环境及需求介绍

  • 环境: Vue2.7
  • 需求:全页面访问、事件点击等全监听

2. 封装逻辑

  • 由于需要监听页面访问事件,为了统一封装,我们将用到 Mixins 混入监听页面的生命周期;
  • 所有的事件发送参数分为公参和特定事件参数,因此我们需要将公参数据进行提取,统一修改。
  • 埋点的核心逻辑,用到的是 GIF 请求发送

使用 GIF 请求发送的原因其实很好理解:

  1. 防止跨域问题:

    为了防止数据请求收发的的地址和项目当前地址不同,而产生跨域问题。所以我们一般使用 <img src='xxx'> 图片进行数据请求。

  2. 防止阻塞页面加载,影响用户体验:

    由于创建资源节点,需要将对象注入到浏览器 DOM 树,浏览器才会发送数据请求。但是仿佛操作 DOM 会产生性能问题,并且反复载入 js/css 资源会阻塞页面渲染。但是 图片请求 能够很好的解决这个问题。在 JavaScriptnew 一个 image 对象就能发起数据请求,并且不会产生阻塞问题。

  3. GIF 体积最小

    与常见的 Jpg 和 PNG 格式的图片相比,GIF 的体积最小:PNG 需要 67 个字节,而 GIF 只需 43 个字节。

3. 封装基础库

  1. 封装 Debug 函数

    由于模块间的独立性,我们将打印数据埋点的 Debug 进行统一封装:

    /**
     * 控制台输出信息
     * @param  {...any} args 输出信息
     */
    const DEBUG_LOG = true // 是否输出采集信息
    export function debug(...args) {
      if (DEBUG_LOG) console.log(...args)
    }
    
  2. GIF 核心请求模块

    const QS = require('querystring')
    const apiGIF = 'http://${project}.${host}/logstores/${logstore}/track.gif'
    
    /**
     * @function 具体的track动作
     * @param {Object} params 具体发送的数据
     * @param {String} apiUrl 发送的接口请求
     */
    export function trackAction(params, apiUrl = apiGIF) {
      /* 如果参数不是字符串则转换为query-string  */
      let _params = typeof params === 'string' ? params : QS.stringify(params)
      /* 创建Image对象来发送请求 */
      /* <img src='http://${project}.${host}/logstores/${logstore}/track.gif?APIVersion=0.6.0&key1=val1&key2=val2'/> */
      let src = `${apiUrl}?${_params}`
      let img = new Image(1, 1)
      debug('发送日志, 内容参数为: ', QS.parse(_params))
      img.src = src
      /* 请求发送, load和error事件监听动作的完成 */
      return new Promise((resolve, reject) => {
        img.onload = function () {
          resolve({ code: 200, data: 'success!' })
        }
        img.onerror = function (e) {
          reject(new Error(e.error))
        }
      })
    }
    
  3. 发送日志实际函数

    const priorParameters = {
     // 公共参数
     count_version: '1.0.0', // 统计版本号
     app_version: '1.0,0', // apk应用版本号
      .....
    }
    
    /**
     * 获取当前时间
     */
    export function getCurrentTime() {
     return new Date().valueOf()
    }
    
    /**
     * 发送日志
     * @param {Object} trackData 特定信息
     */
    export function triggerLog(trackData) {
     let session_id = getCurrentTime() // 毫秒级时间戳,单次使用
     trackData = { ...priorParameters, session_id, ...trackData }
     trackData = QS.stringify(trackData)
     return trackAction(trackData)
    }
    

    由此,我们封装好了,埋点数据收发的主体函数,后续只需要依据特定事件,调用 triggerLog 函数传输特定参数 trackData 就完成了埋点的数据请求。

  4. 利用 Mixins 混入封装页面返回事件

    如点击事件等页面详细事件,不同项目可以做到不同封装,在此不做具体分析了。我们来看看页面返回事件的统一封装。

    混入(Mixins) 可以捕获页面的生命周期,由于切换页面会触发页面的生命周期,因此,我们可以借此完成页面访问的埋点。

    Tips 生命周期遵从“从外到内,再从内到外,mixins 先于组件”的原则

    所以,页面生命周期会先加载 Mixins,而后若有相同的事件,则由页面内部进行覆盖。

    最终的 trackMixins 如下:

    /**
     * @module 组件混入埋点
     */
    import { trigger, debug } from './trackBase'
    
    export const trackMixins = {
      data() {
        return {
          __is_first_create: true, // 是否初次访问
        }
      },
      created() {
        debug('-----------页面创建初始化-----------')
        const pageName = this.$route.name
        this.__triggerPageview()
      },
      activated() {
        if (this.$data.__is_first_create) {
          // 首次访问, 不执行该初始化
          this.$data.__is_first_create = false
          return
        }
        debug('-----------页面重新挂载-----------')
        this.__triggerPageview()
      },
      beforeRouteUpdate(to, from, next) {
        debug('-----------页面即将刷新-----------')
        this.__triggerPageview() // 发送刷新前当页信息事件
        next()
        debug('-----------页面刷新-----------')
        this.__pageRefresh() // 页面刷新, 发送页返事件
      },
      beforeRouteLeave(to, from, next) {
        debug('-----------页面关闭-----------')
        this.__triggerPageview() // 发送页面关闭事件
        next()
      },
      methods: {
        /* ---------页面点击事件---------- */
        mixinTriggerClick(clickData) {
          let trackData = {
            // xxxxx
          }
          debug('-----------发送点击事件-----------')
          return trigger(trackData)
        },
        /* ------------------- 内部封装事件方法 ------------------- */
        /* ---------触发页面访问事件---------- */
        __triggerPageview() {
          // 用户进行页面访问,离开页面时发送该统计请求; 主要用途:记录用户在页面上的浏览操作行为
          let trackData = {}
          // 此处省略时间处理函数
          return trigger(trackData)
        },
        __pageRefresh() {
          // 页面内容更新
          this.__triggerPageview()
        },
      },
    }
    

    如上,我们还将页面点击事件进行了封装。实际项目,我们依据需要进行处理。

  5. 具体页面使用

    <template></template>
    
    <script>
    import { trackMixins, trigger } from '@/log/index.js' // 导入埋点组件
    export default {
      mixins: [trackMixins],
      // .....
    }
    </script>
    

前端代码埋点还是需要依据不同的项目需要,进行设计。核心逻辑依旧是封装 GIF 请求的 src 发送。

笔者只是提供一个思路,感谢你的浏览。

以上。

Loading...