引入友盟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() 取的是系统的时间,如果用户手动更改了系统时间,这个函数将返回错误的值
还没有解决的点
- 基于存在判断条件/依赖于异步请求的前提下,埋点代码还是需要写在业务逻辑里面。