24
头图

Wordy words

As we all know, making C-end products requires extremely high user experience. At the same time, whether the product implementation is in place and whether the return on investment is proportional has become a key factor in whether a team has rewards (this is one of the true demands of a most practical software team" hahaha). Although a lot of user research will be done in the early stage, receipts will be collected, etc., to clarify which are the basic user needs and which are the user's exciting needs, but still need a specific and quantifiable data support to evaluate whether a certain business meets the real user needs Requirements, as well as reference for later iterations of the product, etc.

So based on the above actual situation, for the entire product line, it is very necessary to do a buried point business collection. Of course, there is more significance in burying points, so I won't discuss it here for now.

The following will conduct a comprehensive analysis of the implementation process of the program, and will also provide a simple and reasonable reusable program output. Interested customers please stay here~✌

Current status

  • At present, the three product business lines are managed separately for front-end projects. Existing businesses will also have a steady increase in demand, and new product lines will be added later.
  • Mainly use the Vue.js@2.0 version, but one project was 3.0 version.
  • The project has been developed and iterated for 8 months, and the main product business code has exceeded 170,000 lines.

Overall analysis

  • There is no ready-made solution for third-party docking that can not only meet current needs, but also meet the key elements of strong scalability, and at the same time, the collection of private information cannot be handed over to a third party.
  • The amount of existing code does not have the conditions for precise selling points.
  • PV-type data collection can be temporarily handed over to a certain degree of statistics. This kind of large and general data is only concerned by operations, and the granularity of user analysis is smaller.
  • The embedding point does not affect specific business functions and codes as much as possible, or even absolutely does not affect.

Actual demand

  • Collect users whose granularity is down to the button level.
  • Page entry time, stay time, that is to get enterTime and leaveTime .
  • To have available to a page amount, a press button so the amount of a user use locus, a period button usage, user browser's information, the background is preferably provided with the front page function traffic to visualization reduction.

Turn requirements into data

The following is the rough data that needs to be collected

userAgent // Browser information, including device information, resolution, etc.

path // You can use router or location.href to access the page

pageInfo // page information enterTime and leaveTime

userInfo // user information currently logged in user personal information

eventData // Operation event information event type, operation DOM node, operation time, node name (html node name), node text

How to start the code

  • Without affecting the existing business, it is necessary to separate into the public category.
  • User information and other information that may be recorded later are extensible and should be able to be imported from outside.
  • Currently using the open source UI framework, the overall button nodes of the button tags (some of them can be changed).
  • Data recording and sending. Don’t send too frequently. You need a queue to record data, set a threshold, and send and clear it when the threshold is reached.

Probably the prototype of the code

basic structure

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) 
  }
}

Usage

// 这里建议在项目封装的router管理模块使用
import Monitor from './monitor'

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

Monitor class should have data

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

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

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

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

    // 当前操作队列ID
    this.currentQueneId = null

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

Buried point functional structure

browser information record

Here you can customize to get more information. If you want to load the first screen, network speed detection and other data also need to be collected later, you can also add it.

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

Record page dimension information

Because the final uploaded data is an integrated array object, and the single item of data follows the page, the same page may be accessed repeatedly within the threshold range, so a guid to identify "current page" to ensure uniqueness, guid generated The method is as follows. Generally, the uniqueness can be guaranteed within the threshold, and there is no need to make it too complicated.

/**
* @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)
  })
}

To reach the threshold, you need to upload the existing data and clear the data. The uploaded data should be processed by the outside, so a get() method should be exposed to the outside to obtain the data, and clear() used to clear the data.

Get data

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

Clear data

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

Of course, the method of uploading data internally can also be used here, that is, the interface request method needs to be introduced, and this function is handed over to Monitor processing. Here, you only need to listen to the queue internally at the same time, that is, there can be a vptHandler method.

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

records the basic information of the page visit

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) // 将当前页面数据追加到操作队列
})

Leaving the page to monitor

This is the key element to complete the closed loop of a page data

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

event monitoring

Event monitoring requires a relay. For eventCallback , there should be forwarding methods for different event types. Of course, only click events are currently considered.

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

Click event monitoring

As mentioned above, because the project adopts a unified UI framework as a whole, almost all buttons adopt the standard button label, so the following data can be obtained according to the DOM node for recording

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 event destruction

For specific scenarios, we don’t need to record user behavior, we can Monitor the monitoring event of 060f4c08d63770 to avoid redundant performance consumption

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

Result output

So far we can obtain the data with the following structure and upload it according to the timing set by the threshold. ß

W3TBdO.png

Summarize the results

The above overall plan analyzes the requirements according to various actual conditions, from overall analysis to code landing, combined with the overall characteristics of the project, while taking into account low-cost development and access, to meet later scalability, and even this idea can meet other single-page frameworks.

Similar requirements like this may not be noticed by the product when the team is small, and some products without similar experience can't be started, and they don't know how to collect and display. Therefore, it is often the requirements directly proposed by the project manager to the development, and there is no specific prototype output. At this time, it is necessary to develop to think about the problem from various dimensions, and convert this "one-sentence requirement" into an implementable code plan and apply it to the work. among.

Some parts of the code involved in this article may not be considered comprehensively. At the same time, there may be many other views and discussions on the perspective of problem analysis. More original intentions are to share this method with many colleagues (definitely not the best , Hahahaha), welcome XDM passing by to drop me at any time.

Don't be stingy to leave your footprints 👇


vannvan
699 声望54 粉丝

Be a moral integrity programmer!