要干什么
最近在做前端业务埋点,分享一下怎么写的。
我这边的业务主要有两个地方需要采集:
1、每个页面的访问时间、进入方式、上一页等;2、具体事件的点击
思路:
第一个需求:
监控每个页面的变化,应该在app.js里,用umi.js提供的onRouteChange
记录每次路由改变时的数据。
每次路由变化时,发送上一次页面的数据。
发送数据使用navigator.sendBeacon()
。
tips:
使用 sendBeacon()
方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。
那么问题来了?那最后一次页面的数据怎么办呢?
这就需要用到,window.addEventListener('unload', fn)
,此方法会在app.js被卸载的时候调用,在这里我们发送最后一个页面的数据。
还有一些细节的地方,比如收集页面访问时间。
我的方法是:
- 在最外层定义一个变量
startTime
onRouteChange
方法里,用当前时间减去startTime
,就得到了页面访问时间。- 然后发送数据
- 发送数据后,
onRouteChange
里再把startTime
赋值为当前时间。
这里还有一个小问题,就是如果用户没有退出应用,比如他去了其他的应用,那这个时间应该不算做我们页面的访问时间。
所以使用visibilityChangeEvent
方法,当页面没hidden
时,计算离开页面的时间。然后在onRouteChange
里减去这部分时间。
第二个需求:
采集点击事件,我这里用了一个插件,
链接:reportEvent
但是我们把里面的发送事件变成navigator.sendBeacon
var reportEvent = { _ajax: function (obj) { navigator.sendBeacon(obj.url, obj.data); },
代码实现:
import moment from 'moment';import {localget, localset} from 'utils/index';export const dva = { config: {onError(err) { err.preventDefault(); console.error(err.message);}, },};// 记录页面数据let startTime = 0 ;let historyUrlCode = ['',''];let historyUrl = ['',''];let prePageUrl = '';const FROMAT = 'YYYY-MM-DD HH:mm:ss';let leaveTime = 0, leaveTimeStart = 0, preAction ='';let hiddenProperty = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ? 'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null;let visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');let onVisibilityChange = function(){//应用是否在前台监听console.log(document[hiddenProperty], new Date());if(document.hidden){ // 不在当前页面 leaveTimeStart = new Date();} else { if(leaveTimeStart !== 0){ leaveTime = new Date() - leaveTimeStart; leaveTimeStart = 0 }}}document.addEventListener(visibilityChangeEvent, onVisibilityChange);window.addEventListener('unload', function(event) { const { currentPlatform={}, pageEventList=[] } = window.g_app._store.getState().user; let endTime = new Date(); let showTime = Math.ceil((endTime - startTime - leaveTime)/1000); historyUrlCode = [ UrlToCode(historyUrl[0], pageEventList), UrlToCode(historyUrl[1], pageEventList)]; // 发送的数据 let data ={... } navigator.sendBeacon('url', JSON.stringify(data));});export function onRouteChange({ location, routes, action}) { let showTime,endTime; // 页面访问埋点 if(startTime !== 0){endTime = new Date();showTime = Math.ceil((endTime - startTime - leaveTime)/1000); } // 获取model里的state const { currentPlatform={}, pageEventList=[] } = window.g_app._store.getState().user; historyUrlCode = [ UrlToCode(historyUrl[0], pageEventList), UrlToCode(historyUrl[1], pageEventList)]; localset("historyUrlCode", historyUrlCode); // 发送的数据 let data = {...}; // 发送 if(startTime !== 0) navigator.sendBeacon('url', JSON.stringify(data)); // 进入一个新页面,重置 startTime = new Date(); historyUrl = [historyUrl[1], location.pathname] prePageUrl = location.pathname; leaveTime = 0; preAction = action;}// url转成对应页面编码function UrlToCode (url , codeList) { let res; if(url.includes('/content/')){// 案例,特殊处理res = codeList.filter(item => item.pageUrl === '/content')[0].pageEventCode;console.log(res) } else {res = codeList.filter(item => item.pageUrl === url)res = (res[0] && res[0].pageEventCode) || '' } return res}