1. 动态图片
通过在unload事件处理器中,创建一个图片元素并设置它的 src 属性的方法来延迟卸载以保证数据的发送。因为绝大多数浏览器会延迟卸载以保证图片的载入,所以数据可以在卸载事件中发送。
const reportData = (url, data) => {
let img = document.createElement('img');
const params = [];
Object.keys(data).forEach((key) => {
params.push(`${key}=${encodeURIComponent(data[key])}`);
});
img.onload = () => img = null;
img.src = `${url}?${params.join('&')}`;
};
2. Navigator.sendBeacon
浏览器引入的sendBeacon方法,**发出的是异步请求,但是请求是作为浏览器任务执行的,与当前页面是脱钩的。查看页面后台XHR中是找不到请求的,需要在ALL中才能找到请求
navigator.sendBeacon(url, data);
url 就是上报地址,data 可以是 ArrayBufferView,Blob,DOMString 或 Formdata,根据官方规范,需要 request header 为 CORS-safelisted-request-header,在这里则需要保证 Content-Type 为以下三种之一:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
if(navigator && navigator.sendBeacon){
/**如果数据类型是 string,则可以直接上报,此时该请求会自动设置请求头的 Content-Type 为 text/plain */
return navigator.sendBeacon(url, data)
/**如果用 Blob 发送数据,这时需要我们手动设置 Blob 的 MIME type,一般设置为 */
const blob = new Blob([JSON.stringify(data)], {
type: 'application/x-www-form-urlencoded',
});
return navigator.sendBeacon(url, blob);
/**可以直接创建一个新的 Formdata,此时该请求会自动设置请求头的 Content-Type 为 */
const formData = new FormData();
Object.keys(data).forEach((key) => {
let value = data[key];
if (typeof value !== 'string') {
// formData只能append string 或 Blob
value = JSON.stringify(value);
}
formData.append(key, value);
});
return navigator.sendBeacon(url, formData);
}else{
request({
url: url,
data: params
})
}
sendBeacon 如果成功进入浏览器的发送队列后,会返回true;如果受到队列总数、数据大小的限制后,会返回false。返回ture后,只是表示进入了发送队列,浏览器会尽力保证发送成功,但是否成功了,无法判断。
sendBeacon方法具有如下特点:
发出的是异步请求,并且是POST请求,后端解析参数时,需要注意处理方式;
发出的请求,是放到的浏览器任务队列执行的,脱离了当前页面,所以不会阻塞当前页面的卸载和后面页面的加载过程,用户体验较好;
只能判断出是否放入浏览器任务队列,不能判断是否发送成功;
Beacon API不提供相应的回调,因此后端返回最好省略response body。