网页追踪用户行为
鹿酒 2020/1/19 JavaScript统计监测
统计代码发送方案
# 网页追踪用户行为
公共方法
var config = {
monitor_url: commonConfig.websitePrefix() + '/api/monitor/ut'
}
var tools = {
jsonToParams:function(json, notNeed){
var parameStr = notNeed ? '' : '?';
for(var i in json){
if(typeof json[i] == 'object'){
parameStr += i + '=' + encodeURI(JSON.stringify(json[i])) + '&';
}else{
parameStr += i + '=' + encodeURI(json[i]) + '&';
}
}
parameStr = parameStr.substring(0,parameStr.length - 1);
return parameStr;
}
}
var _monitor = {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 同步请求
使用
XMLHttpRequest发送同步请求的方式已经计划从规范中删除,不再建议开发者使用。
_monitor.blockSend = function(params){
let xhr = new XMLHttpRequest();
xhr.open('post', config.monitor_url, false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(tools.jsonToParams(params, true));
}
1
2
3
4
5
6
2
3
4
5
6
同步请求,需要等待请求返回,阻塞后续js的执行。无论在任何情况下,都不推荐使用,十分影响用户体验。
# 强制延时
_monitor.delayedSend = function(params){
let xhr = new XMLHttpRequest();
xhr.open('post', config.monitor_url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(tools.jsonToParams(params, true));
for (let i = 1; i < 10000; i++) {
for (let m = 1; m < 10000; m++) { continue; }
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
异步请求,通过强制的循环来延长停留时间,没有阻塞,但是仍旧会有卡顿的感觉,不建议。
# 图片请求
_monitor.picSend = function(params) {
var body = document.getElementsByTagName('body')[0];
var monitor_pic = document.createElement('img');
log_pic.src = config.monitor_url + _monitor.jsonToParams(params);
body.appendChild(monitor_pic);
body.removeChild(monitor_pic);
}
1
2
3
4
5
6
7
2
3
4
5
6
7
GET请求,通过访问插入图片的方式通讯,不会阻塞js,但是会有小概率丢失的情景,可以使用。
# Ftech
_monitor.ftechSend = function(params) {
fetch(config.monitor_url, {
method: 'POST',
body: _monitor.jsonToParams(params, true),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'token': 'token'
},
keepalive: true
});
}
_canUseFetch() {
if (navigator && navigator.userAgent) {
// 检测浏览器版本 来决定是否开启 fetch
var userAgent = navigator.userAgent || "";
var appVersion = navigator.appVersion || "";
var vendor = navigator.vendor || "";
var ua = (
userAgent +
" " +
appVersion +
" " +
vendor
).toLowerCase();
var match =
/(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
/(trident)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
(ua.indexOf("compatible") < 0 &&
/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua)) ||
[];
var engine = match[0];
var mainVersion = match[2].split(".")[0];
// chrome 内核版本大于 46, firefox 版本大于39 才开启 fetch
return engine.indexOf("chrome") === 0 && mainVersion >= 46 || engine.indexOf("mozilla") === 0 && mainVersion >= 39;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
fetch的keepalive属性,可以保证页面无论是否关闭,请求都会持续到结束(限制64KB)。解决请求第三次握手前,浏览器关闭,导致的请求失败问题。
# Beacon
底层使用的还是fetch,所以限制都是64KB,优势是简单好用
_monitor.beaconSend = function(params) {
navigator.sendBeacon(config.monitor_url, params);
}
1
2
3
2
3
POST请求,最新选择。相当于把js行为从页面上升到浏览器维度,关闭页面后传输队列中的请求也会发送。兼容性差一点,所以最好搞一个兼容写法。
# 总结
一个兼容写法,尽量减少数据丢失
_monitor.send = function(params) {
var state = navigator.sendBeacon(config.monitor_url, params);
if(!state){
_monitor.picSend(params)
}
}
1
2
3
4
5
6
2
3
4
5
6