作者前段时间在做类似Google Analytics(以下简称GA)的第三方监控脚本。所以对GA的前端代码做过调研,对GA的压缩后代码做了一定程度上的人肉美化。这里美化的是analytics.js的j41版本,本文提到的小技巧也是基于这个版本的js。
GA监控脚本一般都放在开发者的网页上。域名往往和Google不一样,这样发送请求到Google服务器的时候会涉及到跨域。普通的Ajax请求是做不到的,通常称这种请求为beacon或是ping。业内常用的一个方案是发送一个图片请求(GET方式),将请求参数放在图片请求的地址后面。例如:
https://www.google-analytics.com/r/collect?v=1&_v=j46&a=134081920&t=pageview&_s=1&dl=http%3A%2F%2Fzencode.in%2F&ul=zh-cn&de=UTF-8&dt=MZhou%27s%20blog%20-%20Taste%20of%20life.&sd=24-bit&sr=1280x800&vp=481x676&je=0&fl=22.0%20r0&_utma=9582782.1438874051.1425021219.1431473728.1471631422.8&_utmz=9582782.1471631422.8.1.utmcsr%3D(direct)%7Cutmccn%3D(direct)%7Cutmcmd%3D(none)&_utmht=1473782171819&_u=QACCAAABI~&jid=633265686&cid=1438874051.1425021219&tid=UA-36422454-1&_r=1>m=GTM-MP42BH&z=300649187
因为通常第三方监控请求没有很强的安全要求(不会发送密码、密钥之类的信息),所以使用图片请求将请求参数放在地址里面也是OK的。示例代码如下:
function imgPing(url, callback) {
var key = '__SOME_RANDOM_KEY__' + (+new Date());
var img = new Image();
window[key] = img;
img.onload = img.onerror = img.onabort = function () {
img.onload = img.onerror = img.onabort = null;
window[key] = null;
img = null;
if (callback) {
callback();
}
};
img.src = concatUrl;
return true;
}
图片请求是GET请求,参数放在URL地址中,而URL地址的长度是有一定限制的。规范对URL长度并没有要求,但是浏览器、服务器、代理服务器都URL对长度有要求。例如:IE6、7、8(部分)的URL长度不能超过2083的字符长度,URL中的path部分不能超过2048。这就导致有些请求会发送不完全。
为了解决这个问题可以使用XMLHttpRequest
(简称XHR)来发送跨域POST请求。当然这需要浏览器的跨域支持。发送POST请求时,参数都放在请求的payload中,不会受到URL长度所限制。但因为是POST请求,所以需要协议头部比GET方法多一点点,消耗也稍高。示例代码如下:
function xhrPing(url, params, callback) {
if (hasCors()) {
return;
}
var xhr = new window.XMLHttpRequest();
xhr.open('POST', url, true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'text/plain');
if (callback) {
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) {
return;
}
var status = xhr.status;
var isSuccess = status >= 200 && status < 400;
var error = null;
if (!isSuccess) {
error = new Error();
}
callback(error);
};
}
xhr.send(params);
};
除了这两种方法之外,浏览器还提供了一个标准的用于发送beacon的方法:[navigator.sendBeacon](http://)
。这个方法本质上和跨域的XHR请求没有多大区别,但是sendBeacon
方法能够确保在页面关闭的时候还能发送成功。这也是它的最大优势。示例代码如下:
function beaconPing(url, params) {
if (hasSendBeacon()) {
return window.navigator.sendBeacon(url, params);
}
else {
return false;
}
};
在sendBeacon
出现之前,为了能够在页面关闭时发送beacon,常用的方法是两种:
XMLHttpRequest
outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user's experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when entry settings object's global object is a Window
object. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an InvalidAccessError exception when it occurs.
综合上面的讨论给出如下的对比表格:
方法 | 优点 | 缺点 |
---|---|---|
图片请求 | 1. GET请求头部少,快2. 支持广 | 1. URL长度限制2. 需要延迟关闭才能用于unload发送 |
sendBeacon | 1. unload时更能确保成功2. 支持发送更多数据 | 1. POST请求消耗多2. 旧浏览器支持少 |
XHR CORS | 支持发送更多数据 | 1. POST请求消耗多2. 旧浏览器支持少3. unload时不能使用 |
如果没有指定发送方法,那么GA会在URL字符长度不超长时使用图片beacon的方式发送。如果超过了则尝试使用sendBeacon
方法发送beacon请求,如果不支持sendBeacon
则会采用跨域XHR发送。如果跨域XHR不支持则最后fallback到图片发送。实际代码如下:
var smartPing = function(api, param, callback) {
callback = callback || noop;
if (2036 >= param.length) {
imgPing(api, param, callback);
} else if (8192 >= param.length) {
beaconPing(api, param, callback) || xhrPing(api, param, callback) || imgPing(api, param, callback);
} else {
errorPing('len', param.length);
throw new OverLengthError(param.length);
}
};
当然GA的做法并非最优,因为非IE6、7的浏览器图片请求发送的数据可以超过2083。如果真的有很多数据需要发送,那么我们可以合并短请求、拆分长请求。
资料:
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有