需求描述
用户离开页面时,发送一个请求,统计用户的浏览时长。
衍生譬如上报错误等,类似需要在页面卸载的时候发送请求给后台的。解决办法一样。
解决方案
页面进入的时候,记录开始时间,页面卸载的时候,记录结束时间,并且请求接口上报数据。
最早期方案
if ("onpageshow" in window) {
$(window).on("pageshow", loadHandler);
$(window).on("pagehide", unloadHandler);
} else {
$(window).on("load", loadHandler);
$(window).on("unload", unloadHandler);
}
unloadHandler 内处理ajax异步请求。
但是,这种方案的缺陷很大,就是在ios微信浏览器等内,异步请求,不一定能够在页面卸载的时候发送出去。基本上都是被cansel掉了。
优化方法:
改为ajax同步发送。
类似如下:
window.addEventListener('pagehide',()=>{
$.ajax({
url:"http://****",
type:'post',
data:{},
async:false,
dataType:"json",
....
})
})
但是,这种方法在微信浏览器内,ios端,是不生效的。
最终解决方案:Navigator.sendBeacon()
文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/sendBeacon
Navigator.sendBeacon方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是ArrayBufferView
,Blob
,DOMString
或 Formdata
。
对应后台接口 我们大多数这里选择Formdata类干。
// 创建一个新的 FormData 并添加一个键值对,多参数,用append出入
let data = new FormData();
data.append('k1', 'world');
data.append('k2', 'aaaa');
let result = navigator.sendBeacon('api服务器请求地址', data);
if (result) {
console.log('请求成功排队 等待执行');
} else {
console.log('失败');
}
那么,我们可以做降级方案
if (navigator.sendBeacon) {
// Beacon 代码
} else {
// 回退到 XHR同步请求或者不做处理
try {
// get请求
var req = new window.XMLHttpRequest();
req.open('GET', url, false);
req.send();
// post请求
var client = new XMLHttpRequest();
client.open('POST', '/log', false);
client.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
client.send(analyticsData);
} catch (e) {
// Fix IE9 cross-site error
var img = new window.Image();
img.src = url;
}
}
需要注意一点: 我们的接口请求,一般都是
application/json
格式。如果使用navigator.sendBeacon
发送请求时,控制台报错415 (Unsupported Media Type)
, 那么需要后台把接口改一下,接受formData表单提交的形式。
最后,接口请求,并不会出现在chorm 浏览器控制台的xhr内看到。因为,它都不是这个方式。
不报错了,就可以在后台去看到刚刚上报是否成功。当然,前端let result = navigator.sendBeacon 这个接口 ture就是成功了。
参考文档链接:
https://segmentfault.com/a/1190000018271575