在Vue项目中使用友盟进行埋点

引入友盟SDK

首先第一步还是需要引入友盟的SDK。

(function (w, d, s, q, i) {
  w[q] = w[q] || [];
  var f = d.getElementsByTagName(s)[0]; var j = d.createElement(s);
  j.async = true;
  j.id = 'beacon-aplus';
  j.src = '<https://d.alicdn.com/alilog/mlog/aplus/>' + i + '.js';
  f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'aplus_queue', '203467608');

参数配置

然后配置需要的参数。

因为在开发过程中,需要很多的调试环节,因此开启调试模式会非常有用,里面包含上报的数据值等有用信息。

// 集成应用的appKey
aplus_queue.push({
  action: 'aplus.setMetaInfo',
  arguments: ['appKey', your_appKey>]
});

// sdk提供手动pv发送机制,启用手动pv(即关闭自动pv),需设置aplus-waiting=MAN;
// 注意:由于单页面路由改变时不会刷新页面,无法自动发送pv,所以对于单页应用,强烈建议您关闭自动PV, 手动控制PV事件
aplus_queue.push({
  action: 'aplus.setMetaInfo',
  arguments: ['aplus-waiting', 'MAN']
});

// 是否开启调试模式
aplus_queue.push({
  action: 'aplus.setMetaInfo',
  arguments: ['DEBUG', process.env.NODE_ENV !== 'production']
});

上报数据主要的函数

// PV
export function sendPV () {
  const { aplus_queue } = window;
  aplus_queue.push({
    action: 'aplus.sendPV',
    arguments: [{ is_auto: false }]
  });
}
// Event
function uaEvent (eventId, data = {}) {
  const { aplus_queue } = window;
  aplus_queue.push({
    action: 'aplus.record',
    arguments: [eventId, 'CLK', data]
  });
}

事件配置

用于维护事件信息,之后如果有添加/删除/修改某事件的配置信息会更加的省事。

/**
 * 事件ID Enums
 */
 export const U_EVENT_ENUMS = {
		test_eventId: 'ceshiyongdeid'
		//...
 };

/**
 *  pv事件配置信息
 * {
 *    <to_page_name>: // router name
 *    {
 *        key: <event_id>, // required 事件id
 *    }
 * }
 */
export const pvWhiteListMap = new Map([
  [
    'test_router', {
      key: U_EVENT_ENUMS.test_eventId
    }
  ]
	//...
]);

通过路由守卫上报PV事件以及带自定义属性

因为通过 Vue 路由守卫可以快速的拿到当前页面和上级页面的路由信息,因此通过 router.afterEach 方面上报 PV 事件(router.beforeEach 也可)。

router.afterEach((to, from) => {
  const upperPage = from.name;
	
  sendPV();

  uaPVEvent(config.key, { upperPage: upperPage });
  }
});

通过指令或方法上报自定义事件

绑定指令到 DOM 上,添加事件监听器,就上报每次触发的事件。

import _ from 'lodash';

const EVENT_TYPE = [
  'click',
  'mouseenter'
];

export const umEventTrack = {
  bind: (el, { value, modifiers }, vnode) => {
    el._umEventHandler = _.throttle(() => {
      const eventId = value.eventId; // 事件id
      const eventParams = value.eventParams || {}; // 事件参数
      uaEvent(eventId, eventParams);
    }, 10);
    el._umEventType = value.eventType || 'click';
    el.addEventListener(el._umEventType, el._umEventHandler, true);
  },
  unbind: (el) => {
    // remove listener and reset customized data and handler.
    el.removeEventListener(el._umEventType, el._umEventHandler, true);
    el._umEventHandler = null;
    el._umEventType = null;
  }
};

记录页面停留的时间

通过 mixin 的方法注入页面,因此在页面中的方法里面可以直接访问到 getSpendTime 函数。

计算离开页面的时间:通过 visibilitychange 事件监听器获取 visibilityState:hidden/false,来判断页面是否可见,切换 tab 或者浏览器最小化触发,但是发现有其他应用出现在在页面层级上时不会触发。拿到离开和返回的时间点,计算每一次离开的时间差,最后每次的时间差相加得到总共的离开时间。

计算页面停留的时间:事件触发的时间点 – 进入页面的时间点 – 离开页面的时间。

export const recordPageTime = {
  data () {
    return {
      leaveDuration: 0, // 总时长
      enterPageTime: null, // 时间点
      leave_time: null, // 时间点
      return_time: null, // 时间点
      diff_duration: null // 时长
    };
  },
  created () {
    this.enterPageTime = new Date();
    // 出于兼容性原因, 应该使用 document.addEventListener('visibilitychange', ...),而不是window.addEventListener
    document.addEventListener('visibilitychange', this.getLeavePageDuration);
  },
  destroyed () {
    document.removeEventListener('visibilitychange', this.getLeavePageDuration);
  },
  methods: {
    getLeavePageDuration () {
      if (document.visibilityState === 'hidden') {
        this.leave_time = Date.now(); // 离开的时间点
      } 
			if (document.visibilityState === 'visible') {
        this.return_time = Date.now(); // 回来的时间点
				// 每次离开->回来花费的的时间段
        this.diff_duration = this.return_time - this.leave_time;
        this.leaveDuration = this.diff_duration
          ? (this.leaveDuration += this.diff_duration)
          : this.leaveDuration;
      }
    },
    getSpendTime () {
		  // 但是由于Date.now()取的是系统的时间,如果用户手动更改了系统时间,这个函数将返回错误的值
      const duration = Date.now() - this.enterPageTime - this.leaveDuration;
      return duration;
    }
  }
};

遇到的坑

  • 第一次上报属性时会决定该属性的类型(数值型 or 字符型),后续不可更改,更改后的值不会被统计。
  • 属性类型为数字时,属性值值会累加。
  • 自定义事件的属性值是T+1天展示计算结果。
  • moseover的子元素会向上冒泡,导致mouseover事件会触发多次,并且可能导致严重的性能问题,因此将最开始使用的 mouseover 改为了 mouseenter。
  • bind 事件会在DOM节点创建的时候就把数据绑定上,如果数据是异步请求返回的值,将拿到初始定义的值,因此可以考虑在 update 生命周期钩子里更新数据,或者动态绑定一个参数。
  • visibilitychange 事件的监听在 Safari7 上不能兼容 window.addEventListener,应该使用document.addEventListener。
  • 页面刷新可能导致数据上报失败。(猜测可能是友盟上报是异步的关系,在使用 $router.go(0) 刷新页面时,导致的数据上报失败)。
  • PV 事件不能创建自定义属性,因此这次的做法是在发送 PV 事件后,再发送一次自定义事件,事件的 id 和 PV 的 id 一样,并且带上自定属性。
  • Date.now() 取的是系统的时间,如果用户手动更改了系统时间,这个函数将返回错误的值

还没有解决的点

  • 基于存在判断条件/依赖于异步请求的前提下,埋点代码还是需要写在业务逻辑里面。

Leave a Reply

Your email address will not be published. Required fields are marked *