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


猜你喜欢
- view视图import reimport osimport mimetypesfrom wsgiref.util import FileW
- 如下所示:update-alternatives --config python一条简单的命令,如下图所示:来源:https://blog.
- 前言最近在项目中遇到这样一个需求:需要在数据表中检索包含指定内容的结果集,该字段的数据类型为text,存储的内容是json格式,具体表结构如
- 本文实例讲述了js实现带有介绍的Select列表菜单。分享给大家供大家参考。具体如下:带有介绍的Select列表菜单特效代码,并不是导航菜单
- 目录step1 搭建框架step2 填充搭建好了的Promise框架总结step1 搭建框架1. 首先我们需要在这里放置一个promise函
- SpringSecurity? Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解
- 1、权限赋予 说明:mysql部署在服务器A上,内网上主机B通过客户端工具连接服务器A以进行数据库操作,需要服务器A赋予主机B操作mysql
- 一、QQ邮箱SSL发送获取qq授权码ssl发送方式不是使用邮箱密码,而是需要授权码,具体步骤如下:登录发送人qq邮箱>>设置&g
- 导言:在前面的第62章《GridView批量更新数据》里,我们用GridView控件里定制了一个批编辑界面,同样的我们也可以定制一个批添加界
- 1. apply与transform首先讲一下apply() 与transform()的相同点与不同点相同点:都能针对dataframe完成
- 本文实例讲述了JS实现针对给定时间的倒计时功能。分享给大家供大家参考,具体如下:有时候,网站需要一个倒计时的特效来庆祝某些特别的日子。自己也
- 四种基本的函数类型局部变量 就是在函数内部定义的变量【作用域仅局限于函数内部】不同的函数 可以定义相同的局部变量,但是各自用各自的 不会产生
- Python字典的key都可以是什么答一个对象能不能作为字典的key,就取决于其有没有__hash__方法。所以所有python自带类型中,
- 1.启动数据库命令行客户端#linux命令,注意区分大小写mysql2.查询数据库#执行结果:返回所有数据库列表SHOW DATABASES
- MySQL 去除重复数据实例详解有两个意义上的重复记录,一是完全重复的记录,也即所有字段均都重复,二是部分字段重复的记录。对于第一种重复,比
- np.random模块常用的一些方法介绍名称作用numpy.random.rand(d0, d1, …, dn)生成一
- 实例如下:import urllib.requestimport urllib.parseimport jsonwhile True: &n
- 本文实例讲述了PHP+Mysql基于事务处理实现转账功能的方法。分享给大家供大家参考。具体如下:<?php header(
- 购物车是电子商务网站中不可缺少的组成部分,但目前大多数购物车只能作为一个顾客选中商品的展示,客户端无法将购物车里的内容提取出来满足自己事务处
- 如下所示:fp = open(''test2.txt','w') #打开你要写得文件test2.tx