简单的开场白:最近一段时间是经常接触前端所部署的统计代码,360导航目前所用的这款代码也是2010年某位同学写得,由于种种原因,一直想重构却始终未完成这部分工作。这篇小文简单说一下,前端发送统计日志的集中方案及其优劣。
就我所知,页面中要想发起一个http请求有如下两大阵营:
- DOM型选手:如利用<img/><script/>元素的src属性及其他类似方案
- JS型选手:利用javaScript本身的能力(当然要浏览器来支持),即使用 new Image().src 或 XHR等(是否还有其他方式 一时还没想起来)
那么接下来就针对这两大阵营来说明他们在统计日志方面的三种应用。
1. <img src=”" />
如果单纯页面上写入<img/>标签,利用制定好的src去发送请求,这绝对是一个最简单的方式。此方式不依赖于JS只依赖DOM,所以只要能正常解析img标签的浏览器无论web还是wap2.0 都可以正常发送统计日志。但同样也有着缺点,那就是无法动态改变打点的URL,也就无法携带更多的信息。这样做的目的只能依靠服务端日志携带的ip,ua,refererUri,sourceUri来做统计。
该方案总结如下:
优点:不依赖JS 能满足几乎所有苛刻的客户端(浏览器)环境
缺点:不能携带动态的数据信息,也就无法做到对uv的统计,通常用于统计ip及pv
(如果在当前页【同域】或打点的http响应中【适时】通过Header的setCookie设置guid,则请求会带上cookie信息,服务端可以做去重统计出uv 但显然有太多的不适及苛刻,不为通常做法。)
2. new Image().src = “”;
这个方案应该是最普通也最朴实地,奇虎的日志发送方案就是如此。一些统计服务如CNZZ统计,量子统计,百度统计,comscore就是靠此发起请求。然而此方案有着一个致命且常犯的坑,也是多年后大家才逐渐发现得。我知道的最早起源见:
http://hi.baidu.com/meizz/blog/item/a0f4fc0ae9d8be1694ca6b05.html
这个弱点简单的描述是:IE的GC机制会导致部分请求不能发送。
以下引一些详细的解释:
垃圾回收,简单的说就是收回某些无用对象所占用的内存以供重新使用,垃圾回收机制通常有一个优先级较低的线程来维护。在java里采用有向树的方式管理内存,那些从根节点出发不可达(unreachable)的对象即被认为是垃圾回收的目标,这种方式有效避免了循环引用的问题。而COM组件(DOM)和Javascript的垃圾回收机制都是基于引用计数(犀牛书上说javascript1.1是基于引用计数的,1.2应该也是),而且在IE里,Dom对象和Javascript对象的垃圾回收又是相互分离的,所以如果这两种对象出现了循环调用就会导致垃圾回收失效,内存泄露,这是IE内存泄露最常见的一种方式。
浏览器的垃圾回收机制对这种”无主”的对象是毫不客气的回收的
参见:
JScript Memory Leaks 译文
How Do The Script Garbage Collectors Work
相关阅读:
Lua Mark-and-Sweep gc(garbage collection-垃圾收集)实现机制详解
当然,这个缺点是有合适方案解决得。
就是将生成的图片对象挂接至window对象上,如
window.log["xxx"] = new Image();
如果踩到坑此坑,那么随着页面业务的复杂,可能会少统计15%左右的有效数据。
该方案总结如下:
优点: 能自由的改变打点URL,方便携带各类信息如点击坐标 点击元素,guid等,这样一来也可以统计UV。
缺点: 依赖JS,要注意在IE中GC机制的影响。
3. 动态插入script标签
这样的打点方式并不多见,我也只是在艾瑞的统计代码中这样见过。方法就不多描述了,原理就是动态生成一个script标签插入DOM树中。
不过这里要提一下此方案的坑,其实就是在head节点下动态插入script常见的坑了。
head.appendChild(s);//<span style="color: #ff0000;">若是这样的方式插入子节点 就有危险喔</span>
这样的方式在IE6某些版的部分页面(如存在BASE节点)会导致请求可能无法完整发送。解决方案就是采用
head.insertBefore( s, head.firstChild );
这样的方式,将原有的向后追加改为向前插入,这也是业内普遍给出的解决方案。
最早发现此问题是在 Jquery1.4.2的bugFix中 注释有如下
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
BUG案例见:http://bugs.jquery.com/ticket/2709,http://bugs.jquery.com/ticket/4378
该方案总结如下:
优点: 同方案2 (虽能避免GC的坑,但本身又隐含着另一个坑,并无太大优势)
缺点: 依赖JS,依赖DOM,要注意在IE6中动态插入DOM的方式。
三种方案总结下来 发现方案三是最不靠谱得,依赖的条件又多,不说占用资源如何,最重要是也同样带来隐患,并无明显优势。
所以一般我们给出的统计日志打点方案最佳实践如下:
<script>
window.log["pv"] = new Image();
window.log["pv"].src= ".....";
</script>
<noscript><img src="..." /></noscript>
上面是方案1和2的结合,当JS不被支持的时候依然可以降级为使用标签的方式(通常用于统计IP及PV了)。
PS. 这些仅是示范代码,具体怎么用 你懂得。
本次小文到此结束,如后续想到其他,再来补上,晚安!