Navigator sendBeacon页面关闭也能发送请求方法示例
作者:涅槃快乐是金 发布时间:2024-04-22 22:37:44
背景
最近在需求中有一个这样的场景:
需要在页面关闭的时候,用户不需要操作,主动关闭当前订单
当时考虑的方案:在页面关闭的时候,向后端发送一个请求,将这个资源释放掉;
定下方案时,觉得也不是什么难事,觉得谷歌浏览器应该会提供页面关闭的 API 供开发者使用。
经过查找,找到了这么两个 API :beforeunload 和 unload
beforeunload
当浏览器窗口关闭或者刷新时,会触发 beforeunload 事件。当前页面不会直接关闭,可以点击确定按钮关闭或刷新,也可以取消关闭或刷新。
window.addEventListener('beforeunload', function (event) {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = '';
});
该事件会使网页在离开或者刷新的时候弹出一个对话框,给用户一个提示。在这个弹框出现时,该页面是做不了任何操作的,除非把这个弹框关闭。其他页面也只能进行简单的点击浏览操作,键盘是操作不了的。
这个不符合用户无感知的条件
unload
当文档或一个子资源正在被卸载时, 触发 unload 事件。
unload 事件在 beforeunload 事件后触发,这时候文档处于一个什么状态呢?
所有资源都存在,像图片,iframe的等,但是这些资源对于用户来说均不可见,界面上的交互也是无效的.
使用方式和 beforeunload 相同,但是 unload 事件中不能使用确认框,毕竟都已经在卸载了
window.addEventListener('unload', function(event) {
console.log('unload');
});
问题
用户无论刷新还是关闭了页面,因为这两种操作都会调用beforeunload和 unload。
异步请求会被 cancel 掉,导致请求无法发送成功,由于使用的axios进行请求,浏览器有几率关闭异步请求,造成请求无法发出,可以尝试换成同步。
在事件的回调中使用同步的 AJAX 请求。
window.addEventListener('unload', function (event) {
let xhr = new XMLHttpRequest();
xhr.open('post', '/log', false);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('foo=bar');
});
但是谷歌浏览器已经不允许页面关闭期间进行同步的 XMLHTTPRequest(),这条规则适用于 beforeunload、unload、pagehide和visibilitychange这些 API;
为了确保页面在卸载时讲数据发送到服务器,官方建议使用 sendBeacon()或者 Fetch keep-alive
Navigator.sendBeacon
官方链接 https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/sendBeacon
这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向web服务器发送数据。
Beacon API 有以下这样几个特点:
通过 HTTP POST 将少量数据异步传输,可靠性好
这个请求不需要响应,保证在页面的 unload 状态从发起到完成之前被发送。
不会阻塞页面卸载,也就不会影响下一导航的载入
支持跨域
不支持自定义请求头
用法如下:
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
我们一般会用到 DOMString , Blob 和 Formdata 这三种对象作为数据发送到后端,下面以这三种方式为例进行说明。
DOMString
如果数据类型是 string,则可以直接上报,此时该请求会自动设置请求头的 Content-Type 为 text/plain。
const reportData = (url, data) => {
navigator.sendBeacon(url, data);
};
Blob
如果用 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
可以直接创建一个新的 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);
};
注意这里的 JSON.stringify 操作,服务端需要将数据进行 parse 才能得到正确的数据。
Fetch keep-alive
当使用fetch() 方法时,如果把keeplive 设置为true,即便页面被终止请求也会保持连接。
window.onunload = function() {
fetch('/analytics', {
method: 'POST',
body: "statistics",
keepalive: true
});
};
来源:https://www.jianshu.com/p/191b64767e77
猜你喜欢
- 自动扫雷一般分为两种,一种是读取内存数据,而另一种是通过分析图片获得数据,并通过模拟鼠标操作,这里我用的是第二种方式。一、准备工作1.扫雷游
- 本文实例讲述了MySQL查看、创建和删除索引的方法。分享给大家供大家参考。具体如下:1.索引作用在索引列上,除了上面提到的有序查找之外,数据
- UserAgent = Trim(Lcase(Request.Serve
- Real Numbers实数实数是具有小数部分的数字, 当然, 实数不是专门用来表示小数的, 也可以用DECIMAL来存储那些无法用INTE
- 本文实例为大家分享了微信小程序实现点击出现弹窗的具体代码,供大家参考,具体内容如下1.现在page文件里面定义一个dh的文件,然后在comp
- 概念如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是平时所说的不需要回表操作判断标准使用expl
- 本文实例讲述了php递归删除目录与文件的方法。分享给大家供大家参考。具体实现方法如下:<?phpfunction deldir($pa
- MediaPipe概述谷歌开源MediaPipe于2019年6月首次推出。它的目标是通过提供一些集成的计算机视觉和机器学习功能,使我们的生活
- 需求小编通常会上一些专业的视频网站比如腾讯视频、优酷,在上面看电影、电视剧。这些网站有个优点,可以缓存视频,在通勤路上比如地铁就可以愉快的刷
- 1.mysql中or语法的使用,在mysql语法中or使用注意点。 项目遇到坑,遍历发放奖励数据查询错误!!!$sql = 'SEL
- go-ini的分区go-ini的多个配置项通过分区(section)来划分。有默认(空)分区和命名的分区,没有给分区命名就是默认分区,默认分
- calccalc 是一个我们想要做剖析(性能分析)的异步函数。按照惯例,它的最后一个参数是一个callback。我们像这样使用 calc:c
- 一、前言你知道当我们在网页浏览器的地址栏中输入 URL 时,Web 页面是如何呈现的吗?Web 界面当然不会凭空出来,根据 Web 浏览器地
- QSlider 是一个具有可来回拉动手柄的控件。有时使用滑块比输入数字或使用旋转框更方便。在我们的例子中,我们将创建一个滑块和一个标签。标签
- 这篇文章主要介绍了微信小程序 textarea 层级过高问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习
- 要想从命令行启动mysqld服务器,你应当启动控制台窗口(或“DOS window”)并输入命令:C
- Android客户端和PHP、MySQL搭建的服务器之间的简单交互,实现登录功能 。实现原理图:Handler消息机制原理:Handler机
- (一)前言这几天供应商在测试环境上使用MYSQL数据库做开发时遇到一个SQL性能问题,即在他开发环境本地跑SQL速度很快就一两秒时间,但是同
- python序列类型包括哪三种python序列类型包括:列表、元组、字典列表:有序可变序列创建:userlist = [1,2,3,4,5,
- 在 Python 中,if 语句用于根据条件执行不同的代码块。它的基本格式如下:if condition: # 如