文章来源: blog.csdn.net/sd19871122/…
上报数据的方法
1 直接将数据通过ajax发送给后端
axios.post(url, data);
劣势:
在页面卸载或刷新时进行数据上报,请求可能会在浏览器关闭或重新加载前还未发送到服务器就被浏览器cancel掉,导致数据上报失败。(TO TEST)
解决:
将ajax改为同步,由于axios不支持同步,使用XMLHttpRequest
const syncReport = (url, data, headers) => {
const xhr = new XMLHttpRequest();
xhr.open('post', url, false) // 同步请求
xhr.withCredentials = true
Object.keys(headers).forEach(key => {
xhr.setRequestHeader(key, headers[key])
})
xhr.send(JSON.stringify(data))
}
但因为是同步,会堵塞页面关闭或重新加载的过程,影响用户体验
2 动态图片
通过在beforeunload
事件处理器中创建一个图片元素并设置它的src
属性的方法延迟卸载以保证数据的发送,因为绝大数浏览器会延迟卸载以保证图片的载入,所以数据可以在卸载事件中发送。
const imgReportData = (url, data) => {
const 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('&')}`
}
此时服务端可以返回一个 1px * 1px 的图片,保证触发 img
的 onload
事件,但如果某些浏览器在实现上无法保证图片的载入,就会导致上报数据的丢失。
3 sendBeacon
为了解决上述问题,便有了 navigator.sendBeacon 方法,使用该方法发送请求,可以保证数据有效送达,且不会阻塞页面的卸载或加载,并且编码比起上述方法更加简单。
用法如下:
navigator.sendBeacon(url, data);
url就是上报的地址
- 如果data数据类型为string,可以直接上报,请求会自动设置请求头的content-type为text/plain
const reportData = (url, data) => {
navigator.sendBeacon(url, data);
};
- 如果用
blob
(表示二进制类型的大对象)发送数据,需要手动设置Blob
的 MIME type,一般设置为application/x-www-form-urlencoded
。
const reportData = (url, data) => {
const blob = new Blob([JSON.stringify(data), {
type: 'application/x-www-form-urlencoded',
}]);
navigator.sendBeacon(url, blob);
};
- 可以直接创建一个新的
Formdata
,此时该请求会自动设置请求头的Content-Type
为multipart/form-data
。
const reportData = (url, data) => {
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);
});
navigator.sendBeacon(url, formData);
};
总结
我们可以使用 sendBeacon
发送数据,这一方法既能保证数据可靠性,也不影响用户体验,如果浏览器不支持该方法,则可以降级使用同步的 ajax 发送数据。