用户卸载网页的时候,有时需要向服务器发一些数据。很自然的做法是在unload事件或beforeunload事件的监听函数里面,使用XMLHttpRequest对象发送数据。但是,这样做不是很可靠,因为XMLHttpRequest对象是异步发送,很可能在它即将发送的时候,页面已经卸载了,从而导致发送取消或者发送失败。
解决方法就是 AJAX 通信改成同步发送,即只有发送完成,页面才能卸载。但是,很多浏览器已经不支持同步的 XMLHttpRequest 对象了(即open()方法的第三个参数为false)。
window.addEventListener('unload', logData, false);
function logData() {
var client = new XMLHttpRequest();
// 第三个参数表示同步发送
client.open('POST', '/log', false);
client.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
client.send(analyticsData);
}
上面代码指定XMLHttpRequest同步发送,很多浏览器都已经不支持这种写法。
同步通信有几种变通的方法。一种做法是新建一个元素,数据放在src属性,作为 URL 的查询字符串,这时浏览器会等待图片加载完成(服务器回应),再进行卸载。另一种做法是创建一个循环,规定执行时间为几秒钟,在这几秒钟内把数据发出去,然后再卸载页面。
这些做法的共同问题是,卸载的时间被硬生生拖长了,后面页面的加载被推迟了,用户体验不好。
使用 sendBeacon()
方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。此外,代码实际上还要比其他技术简单许多!
window.addEventListener('unload', logData, false);
function logData() {
navigator.sendBeacon('/log', analyticsData);
}
Navigator.sendBeacon
方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等等)。
navigator.sendBeacon(url, data)
这个方法的返回值是一个布尔值,成功发送数据为true,否则为false。
该方法发送数据的 HTTP 方法是 POST,可以跨域,类似于表单提交数据。它不能指定回调函数。
window.addEventListener('unload', logData, false);
function logData() {
navigator.sendBeacon("/log", analyticsData);
}