乐趣区

关于vue.js:老大喊我做一个项目埋点方案我给他这么搞做前端的都用得上

啰里八嗦的话

家喻户晓,做 C 端产品对用户体验的要求是极高的。同时产品施行是否到位,投入回报是否成正比,成了一个团队是否有处分的关键因素(这是一个最理论的软件团队的实在诉求“之一“哈哈哈)。只管后期会做泛滥的用户调研,收集收据等等,明确哪些是用户根底需要,哪些是用户兴奋需要,但还是须要一个具体的能够量化的数据撑持,来评估某项业务是否满足了用户实在的需要,以及为产品前期迭代做参考等等。

那么基于以上的理论状况,对于整个产品线而言,做一个埋点业务收集是十分必要的。当然做埋点还有更多的意义,在这里暂不展开讨论。

以下将对该计划施行过程进行全面的剖析,同时也将提供一个简略的正当的可复用的计划输入,感兴趣的客官请留步~✌

以后现状

  • 目前 3 个产品业务线产品,对于前端我的项目而言是离开治理的,现有业务也会有源源不断的需要减少,前面还会减少新的产品线。
  • 次要应用 Vue.js@2.0 版本, 但有一个我的项目是采纳 3.0 版本开发的。
  • 我的项目已开发迭代历经 8 个月,次要产品业务代码已超 17 万行。

整体剖析

  • 对接第三方没有能够既能够满足当下需要,又能够满足强扩展性的要害因素的现成计划,同时对于隐衷信息的收集不能交给第三方。
  • 现有代码量不具备可精准卖点的条件。
  • pv 类型的数据收集可临时交给某度统计,这种大而抽象的数据只有经营会关注,埋点对用户剖析的粒度更小。
  • 埋点尽量不影响具体业务性能及代码,甚至相对的不影响。

理论需要

  • 收集粒度到按钮级别的用户。
  • 页面进入工夫,停留时间,也就是要拿到 enterTimeleaveTime
  • 要具备可对某 页面 使用量,某按 钮使 用量,某用户 应用轨迹 ,某 时间段 按钮使用量,用户 浏览器 根本信息,后盾最好具备前端页面性能点击量的 可视化 还原。

把需要转换成数据

以下是大抵的须要收集到的数据

userAgent // 浏览器信息,蕴含设施信息,分辨率等

path // 拜访页面 能够用 router 也能够用 location.href

pageInfo // 页面信息 enterTime 和 leaveTime

userInfo // 用户信息 以后登录用户个人信息

eventData // 操作事件信息 事件类型,操作 DOM 节点,操作工夫,节点名称(html 节点名称),节点文本

代码怎么下手

  • 不影响现有业务,抽离成公共类是必须的。
  • 用户信息以及其余前期可能记录的属于扩展性信息,应能够内部传入。
  • 目前应用开源 UI 框架,我的项目整体 按钮 节点都是 button 标签(局部不是的能够改过来)。
  • 数据记录和发送,发送不能太频繁,须要一个记录数据的队列,设定一个阈值,达到阈值后进行发送并清空。

代码大略的雏形

根本构造

class Monitor {constructor() { }
  /**
   * @description 初始化办法:
   *    extentData 用于传入基于业务的数据信息,*    router 是 vue-router 对象, 这里既能够通过 init 传入,也能够在以后类模块间接引入,*    config 是配置信息
   * @param {*} {extentData = null, router = null, config = {} }
   * @memberof Monitor
   */
  init({extentData = null, router = null, config = {} }) {
        //TODO
    // 这里能够做一些笼罩默认配置,初始化监听事件等操作
    let {vpt} = config
    this.vpt = vpt ? vpt : this.vpt
    this.uaHandler()
    
    // 这里上面会讲
    this.eventHandler = this.eventCallback.bind(this) // 要害
    document.addEventListener('click', this.eventHandler, true) 
  }
}

应用形式

// 这里倡议在我的项目封装的 router 治理模块应用
import Monitor from './monitor'

const monitor = new Monitor()
monitor.init({
  router: router,
  extentData: {
    userInfo: {
      userName:"张三",
      account:"13666666666"
    }
  }
})

Monitor类应具备的数据

 constructor() {
    // 发送队列的阈值
    this.vpt = 10

    // 事件节点类型限度
    this.limitNodeType = ['button']

    // 用户浏览器信息
    this.uaInfo = {}

    // 页面级别的数据队列
    this.pageDataQuene = []

    // 以后操作队列 ID
    this.currentQueneId = null

    // 此属性用于保留 bind 返回的匿名函数
    this.eventHandler = null
 }

埋点性能构造

浏览器信息记录

这里能够自定义获取更多的信息,前面如果对首屏加载,网速检测等数据也有收集需要也能够加进去。

 /**
  * @description ua 信息记录
  * @memberof Monitor
 */
uaHandler() {
  this.uaInfo = {
    userAgent: navigator.userAgent,
    dpiWidth: window.screen.width,
    dpiHeight: window.screen.height
  }
}

记录页面维度信息

因为最终上传数据是一个整合的数组对象,而单项数据又是跟着页面走的,同一个页面可能在阈值范畴内重复拜访,所以须要一个 guid 用于标识 ”以后页面“ 确保唯一性,guid生成办法如下,个别在阈值内可保障唯一性,没必要搞得太简单。

/**
* @description 生成 guid,以后操作队列的惟一标识
* @returns {*}
* @memberof Monitor
*/
guid() {return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {var r = (Math.random() * 16) | 0,
        v = c == 'x' ? r : (r & 0x3) | 0x8
    return v.toString(16)
  })
}

达到阈值须要上传现有数据并清空数据,上传数据应该是内部自行处理的,所以应向内部裸露一个 get() 办法用于获取数据,clear()用于清空数据

获取数据

/**
   * @description 用于内部获取操作队列
   * @returns {*} Array[]
   * @memberof Monitor
   */
get() {return this.pageDataQuene}

清空数据

 /**
   * @description 用于清空队列
   * @memberof Monitor
   */
clear() {
  //TODO
  this.pageDataQuene = []}

当然这里也能够采纳外部上传数据的办法,就是须要引入接口申请办法,把这项性能交给 Monitor 解决,这里仅需在外部同时监听队列即,如是能够有 vptHandler 办法

/**
   * @description 阈值监听,达到阈值就发送数据
   * @memberof Monitor
   */
vptHandler() {if (this.pageDataQuene.length >= this.vpt) {this.sendData()  // 这里用于走上传数据业务,上传完毕后清空数据,可依据理论状况进行解决
  }
}

记录页面拜访的根本信息

router.afterEach(async (to) => {
  // 来到监听
  this.updateLeaveTime()
  // 阈值监听
  this.vptHandler()
    // 以后操作页面的惟一标识
  this.currentQueneId = this.guid()
  let initPageData = [
    {
      id: this.currentQueneId,
      path: to.path, // 因为以后我的项目目录构造和菜单路由命名是高度一致的,所以能够采纳 path 进行记录
      uaInfo: this.uaInfo,
      pageInfo: {entryTime: this.getTime() // 留神这里只有 enterTime,更新 leaveTime 的机会交给👆下面的 updateLeaveTime 办法
      },
      ...extentData,
      eventData: [] // 事件记录置空}
  ]
  this.pageDataQuene = this.pageDataQuene.concat(initPageData) // 将以后页面数据追加到操作队列
})

来到页面监听

此时是实现一个页面数据的闭环的要害因素

/**
   * @description 页面来到工夫更新
   * @memberof Monitor
   */
updateLeaveTime() {
  let index = this.pageDataQuene.findIndex((el) => el.id == this.currentQueneId
  )
  if (index >= 0) {this.pageDataQuene[index].pageInfo.leaveTime = this.getTime()}
}

事件的监听

事件的监听须要一个直达,对于 eventCallback 应该具备针对不同事件类型办法的转发,当然目前只思考点击事件。

/**
   * @description 事件回调直达
   * @param {*} e
   * @memberof Monitor
   */
  eventCallback(e) {if (e.type == 'click') {this.clickEventHandler(e)
    }
  }

点击事件的监听

下面提到因为我的项目整体采纳对立的 UI 框架,简直所有的按钮都是采纳的规范 button 标签,故能够依据 DOM 节点获取到如下数据进行记录

const {innerText, localName, formAction, type} = ele.target

 /**
   * @description 页面点击事件收集
   * @param {*} ele 事件节点
   * @memberof Monitor
   */
clickEventHandler(ele) {const { innerText, localName, formAction, type} = ele.target
  let isEv = this.limitNodeType.includes(localName) 
  if (isEv) {
    let eventData = [
      {
        innerText,
        localName,
        formAction,
        eleType: type,
        eventType: 'click',
        clickTime: this.getTime()}
    ]
        
    // 找到以后页面中的 eventData,将以后操作追加进去
    let index = this.pageDataQuene.findIndex((el) => el.id == this.currentQueneId
    )
    if (index >= 0) {this.pageDataQuene[index].eventData = this.pageDataQuene[index].eventData.concat(eventData)
    }
  }
}

监听事件销毁

针对特定场景咱们不须要记录用户的行为,能够对 Monitor 的监听事件进行销毁, 以防止多余的性能耗费

// 销毁监听事件
destroy() {document.removeEventListener('click', this.eventHandler, true)
}

后果输入

至此咱们能够获取到如下构造的数据,依据阈值设定的机会进行上传。ß

总结成绩

以上整体计划依据各项理论状况对需要进行了剖析,从整体剖析到代码落地,联合我的项目整体特点,同时兼顾低成本开发与接入,满足前期扩展性,甚至该思路能够满足其余单页面框架。

像这种相似的需要,在团队规模不大的状况下,可能不会被产品关注到,有些没有相似教训的产品也无从下手,不晓得怎么收集,怎么展现。所以往往是项目经理去向开发间接提出的需要,也没有具体的原型输入,这时候就须要开发去从各种维度思考问题,将这种“一句话需要”转化成可施行的代码计划利用到工作当中。

此文章波及到的代码某些局部思考的可能不算周全,同时对问题剖析的角度可能还有泛滥其余的认识和探讨,更多的本意还是将这种办法分享给诸多同行(必定不是最好,哈哈哈哈),欢送路过的 XDM 随时滴我。

不要悭吝留下你的足迹哈👇

退出移动版