要干什么
最近在做前端业务埋点,分享一下怎么写的。
我这边的业务主要有两个地方需要采集:
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
}