埋点数据采集方案

为什么要设计这个程序?

埋点上报所需的数据可能来自四面八方,为了不影响业务,我们采用采集数据的形式单独维护埋点所需的数据,而非从不同地方传进来。

初步想法

封装一个类,主要有采集和上报两个方法,通过事件名为key来对应收集的数据。

class Track {
  constructor() {
    this.eventDataMap = new Map();
  }

  collect(eventName, eventData) {
    this.eventDataMap.set(eventName, eventData);
  }

  report(eventName) {
    const eventData = this.eventDataMap.get(eventName);

    fetch('xxx', {
      event_name: eventName,
      event_data: eventData,
    }).then(() => {
      this.eventDataMap.delete(eventName);
    });
  }
}

export default new Track();

问题

以上程序只适合一对一场景,也就是一个eventName对应一次上报。

track.collect('EVENT_A', {tab: 'A'});

track.report('EVENT_A');

track.report('EVENT_A');

第二次上报的时候数据就没了

问题的本质

想要的是一对一,实际是一对多。

思考

  • 如果是一个事件名对应一处上报,那就简单了,它的生命周期就是收集数据、上报、回收数据。

  • 但如果一个事件名对应多处上报,那就不知道何时回收数据了。

例如:

eventName: ‘login_click’
eventData: {btn_name:‘我的、注册、发送验证码…’, page_source:‘xxx’}

一个事件会对应不同的传参

收集数据的目的?

达到埋点数据和业务数据之间的解耦,到实际上报处读取数据进行上报。

解决

因为收集的可能是公共数据,不知道何时回收该公共数据,那他就不应该被回收。

class Track {
    constructor() {
      this.eventDataMap = new Map();
      this.eventName = ''; 
      this.tempPageSource = {};
      this.taskQueue = [];
    }
  
    /*
     * 采集数据
     * 有需要在不同地方采集数据才使用
     * */
    collect(eventName, eventData = {}) {
      const preData = this.eventDataMap.get(eventName) ?? {};
  
      this.eventName = eventName;
      this.eventDataMap.set(eventName, {
        ...preData,
        ...eventData,
      });
  
      return this;
    }
  
    /*
     * 获取数据
     * */
    getEventData(eventName) {
      return this.eventDataMap.get(eventName) ?? {};
    }
  
    /*
     * 上报
     * 会合并collect过的数据
     * */
    report(eventName, eventData = {}) {
      const _eventName = eventName ?? this.eventName;
      const _eventData = {
        ...(this.getEventData(_eventName) ?? {}),
        ...eventData,
      };
  
      this.addTask(_eventName, _eventData);
      this.run();
    }
  
    run() {
      if (!this.taskQueue.length || !mfUtil.isInit) return;
  
      while (this.taskQueue.length) {
        const task = this.taskQueue.shift();
  
       fetch('...')
      }
    }
  
    /*
     * 
     * 由于初始化的原因可能一开始无法上报,所以加入队列
     * */
    addTask(eventName, eventData) {
      this.taskQueue.push({
        eventName,
        eventData,
      });
    }
  }
  
  export default new Track();

额外

如何收集一个页面来源参数“page_source”
比如点击一个按钮,跳转到另一个页面,需要记录这个按钮的名称。
如果你的页面层级只有两级,那么只需要全局维护一个“page_source”变量即可。
如果有更深的层级则不行,此时的“page_source”就是一对一的关系了,所以可以维护一个map,使页面与page_source一一对应。

一点伪代码
<Button 
  name={'按钮名称'}
  ...
  />
  
const Button=({name})=>{
   return <button
	    	onClick={()=>{
	      	  track.tempPageSource=name
	    	}}
	    />
}

 
 routeChange={()=>{
  const currentName=getCurrentRoute().name
  track.pageSourceMap.set(currentName,track.pageSource)

你可能感兴趣的:(JavaScript,前端)