百度,google,友盟等的统计都用的是 <img src="...">
的方式来统计信息,
前端用GIF打点
why,从原理说起
前端监控原理
由Web页面将用户信息(UA/鼠标点击位置/页面报错/停留时长/etc)等 上报给服务器
上报数据url_encode(百度统计/CNZZ)或JSON编码(神策/诸葛io)为字符串,通过url参数传递给后台服务器
关键在于两点
- 收集用户信息
- 上报数据,只要能上报,其实选择有多种,如下图
因此需要一种方式,尽可能的满足一下条件
- 不关注返回结构
- 传递信息尽可能小
- 不能阻塞web的正常业务,且影响最小
用排除法来看(大家都用git是有原因的)
不用GET/POST/HEAD请求上报?
请求都会涉及跨域。而跨域请求很容易出现由于配置不当,被浏览器拦截并报错。所以排除,且对于后端而言开放所有的跨域拦截是有安全风险的,并且由于要做跨域校验,是有性能消耗的。
为什么不用其他文件(js/css/ttf)
js等的加载只能插入到html文档内部,才能被加载,这个会阻塞浏览器的页面渲染,且改变dom结构,即使之删除了,操作dom也是相当消耗浏览器性能的,影响用户体验。所以排除。
所以选择图片
图片不仅不用插入DOM元素内,且在JS中直接new一个Image就能发起请求。而且还没有阻塞问题。就是没有js的环境下也能通过直接加载img标签来正常打点。这是其他资源所做不到的。
为什么选择GIF
原因: GIF是传输最小的合法图片格式(详情参考:图片类型解析)。
总结:用GIF的原因
- 没有跨域问题
- 不会阻塞页面加载,影响用户体验
- 在所有的图片中提及最小,比较BMP/PNG,可以节省41%/35%的网络资源
以为一切完了吗?没有好戏才真正开始,上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<img src="http://0.0.0.0:8000/logo.png?from=0" id="image0" />
<img src="http://0.0.0.0:8000/logo.png?from=1" id="image1" />
</body>
<script>
var unique = (function() {
var time = new Date().getTime() + '-',
i = 0;
return function() {
return time + i++;
};
})();
window.addEventListener('beforeunload', function(e) {
var data = window['imgLogData'] || (window['imgLogData'] = {});
var uid = unique();
image0.src = 'http://0.0.0.0:8000/logo.png' + '?from=2_uid=' + uid;
data[uid] = image0;
image0.onload = image0.onerror = function() {
image0.onload = image0.onerror = null;
image0 = null;
delete data[uid];
};
var uid = unique();
image1.src = 'http://0.0.0.0:8000/logo.png' + '?from=3_uid=' + uid;
data[uid] = image1;
image1.onload = image1.onerror = function() {
image1.onload = image1.onerror = null;
image1 = null;
delete data[uid];
};
xhr2 = new XMLHttpRequest();
xhr2.open(
'GET',
'http://0.0.0.0:8000/logo.png' + '?from=4&_uid=' + unique(),
true
);
xhr2.send(null);
xhr3 = new XMLHttpRequest();
xhr3.open(
'GET',
'http://0.0.0.0:8000/logo.png' + '?from=5&_uid=' + unique(),
true
);
xhr3.send(null);
xhr4 = new XMLHttpRequest();
xhr4.open(
'GET',
'http://0.0.0.0:8000/logo.png' + '?from=6&_uid=' + unique(),
true
);
xhr4.send(null);
return ((e | window.event).returnValue = '123');
});
window.addEventListener('unload', function(e) {
console.dir(123);
});
</script>
</html>v
这个代码,就是检测统计时在刷新时的图片加载顺序情况。如图(注:logo是随便的一个普通图片,可以替换成其他的,加uid是为了每次有用新的图片)
chrome 测一下
form: 4->5->6->2->3->0->1
发现什么了吗?xhr竟然比js内src要先发出(实效性很小,目前没搞清楚为什么,可能和浏览器事件执行有关系,xhr的队列比js内src请求队列,要有先执行,dom内src请求队列最后执行),而dom内src最后才发出。如果不考虑安全,跨域因素,真的用ajax要更好,而且ajax后端能返回204状态码,没有返回内容,这更好。
safari 测一下
form: 2->3->4->5->6->0->1
调整xhr的调用顺序
form: 4->2->3->5->6->0->1
从图中看出safari对xhr和js内部src没有做区分,但是报错了,浏览器任务不安全,应为request的请求头中没有指名服务器信息,和请求格式等。由安全性问题,所以选择js内src最好,