在Web关闭页面时发送Ajax请求的实现方法


Posted in Javascript onMarch 07, 2019

前言

有时候我们需要在用户离开页面的时候,做一些上报来记录用户行为。又或者是发送服务器ajax请求,通知服务器用户已经离开,比如直播间内的退房操作。

本文主要分两部分来讲解怎么完成退出行为的上报。

1.事件监听

浏览器有两个事件可以用来监听页面关闭,beforeunload和unload。

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则是在页面已经正在被卸载时发生,此时文档所处的状态是:1.所有资源仍存在(图片,iframe等);2.对于用户所有资源不可见;3.界面交互无效(window.open, alert, confirm 等);4.错误不会停止卸载文档的过程。

基于以上两个方法就可以实现对页面关闭的事件监听了,为了稳妥,可以两个事件都监听。然后对监听函数做处理,让关闭事件只调用一次。

2.请求发送

有了上面的监听,事情只完成了一半,如果我们在监听中直接发送ajax请求,就会发现请求被浏览器abort了,无法发送出去。在页面卸载的时候,浏览器并不能保证异步的请求能够成功发出去。

我们有几种方式可以解决这个问题:

方案1: 发送同步的ajax请求

var oAjax = new XMLHttpRequest();

oAjax.open('POST', url + '/user/register', false);//false表示同步请求

oAjax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

oAjax.onreadystatechange = function() {
 if (oAjax.readyState == 4 && oAjax.status == 200) {
 var data = JSON.parse(oAjax.responseText);
 } else {
 console.log(oAjax);
 }
};

oAjax.send('a=1&b=2');

这种方式虽然有效,但是用户需要等待请求结束才可以关闭页面。对用户的体验不好。

方案2:发送异步请求,并且在服务端忽略ajax的abort

虽然异步请求会被浏览器abort,但是如果服务端可以忽略abort,仍然正常执行,也是可以的。比如PHP有ignore_user_abort函数可以忽略abort。这样需要改造后台,一般不太可行..

方案3:使用navigator.sendBeacon发送异步请求

根据MDN的介绍:

这个方法主要用于满足 统计和诊断代码 的需要,这些代码通常尝试在卸载(unload)文档之前向web服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而, 对于开发者来说保证在文档卸载期间发送数据一直是一个困难。因为用户代理通常会忽略在卸载事件处理器中产生的异步 XMLHttpRequest 。

从介绍上可以看出,这个方法就是用来在用户离开时发请求的。非常适合这种场景。

使用方式是这样的:

navigator.sendBeacon(url [, data]);

sendBeacon支持发送的data可以是ArrayBufferView, Blob, DOMString, 或者 FormData 类型的数据。

下面是几种使用sendBeacon发送请求的方式,可以修改header和内容的格式,因为一般和服务器的通信方式都是固定的,如果修改了header或者内容,服务器就无法正常识别出来了。

(1)使用Blob来发送

使用blob发送的好处是可以自己定义内容的格式和header。比如下面这种设置方式,就是可以设置content-type为application/x-

blob = new Blob([`room_id=123`], {type : 'application/x-www-form-urlencoded'});
navigator.sendBeacon("/cgi-bin/leave_room", blob);

在Web关闭页面时发送Ajax请求的实现方法

(2)使用FormData对象,但是这时content-type会被设置成"multipart/form-data"。

var fd = new FormData();
fd.append('room_id', 123);
navigator.sendBeacon("/cgi-bin/leave_room", fd);

在Web关闭页面时发送Ajax请求的实现方法

(3)数据也可以使用URLSearchParams 对象,content-type会被设置成"text/plain;charset=UTF-8"

var params = new URLSearchParams({ room_id: 123 })
navigator.sendBeacon("/cgi-bin/leave_room", params);

在Web关闭页面时发送Ajax请求的实现方法

通过尝试,可以发现使用blob发送比较方便,内容的设置也比较灵活,如果发送的消息抓包后发现后台没有识别出来,可以尝试修改内容的string或者header,来找到合适的方式发送请求。
参考链接:

  • sendBeacon API not working temporarily due to security issue, any workaround?
  • Sending AJAX Data when User Moves Away / Exits from Page
  • Setting HTTP Headers in a Beacon Request

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
通过JAVASCRIPT读取ASP设定的COOKIE
Feb 15 Javascript
一个可以兼容IE FF的加为首页与加入收藏实现代码
Nov 02 Javascript
JavaScript的单例模式 (singleton in Javascript)
Jun 11 Javascript
JS代码放在head和body中的区别分析
Dec 01 Javascript
javascript面向对象入门基础详细介绍
Sep 05 Javascript
JS自动倒计时30秒后按钮才可用(两种场景)
Aug 31 Javascript
浅谈javascript中遇到的字符串对象处理
Nov 18 Javascript
jQuery插件HighCharts绘制的2D堆柱状图效果示例【附demo源码下载】
Mar 14 Javascript
浅谈React Native 中组件的生命周期
Sep 08 Javascript
基于vue和react的spa进行按需加载的实现方法
Sep 29 Javascript
从零搭一个自用的前端脚手架的方法步骤
Sep 23 Javascript
原生javascript制作贪吃蛇小游戏的方法分析
Feb 26 Javascript
mpvue微信小程序多列选择器用法之省份城市选择的实现
Mar 07 #Javascript
使用vue开发移动端管理后台的注意事项
Mar 07 #Javascript
vue插件mescroll.js实现移动端上拉加载和下拉刷新
Mar 07 #Javascript
从0到1构建vueSSR项目之node以及vue-cli3的配置
Mar 07 #Javascript
从0到1构建vueSSR项目之路由的构建
Mar 07 #Javascript
bootstrap-table实现表头固定以及列固定的方法示例
Mar 07 #Javascript
js如何获取图片url的Blob值并预览示例代码
Mar 07 #Javascript
You might like
ThinkPHP中URL路径访问与模块控制器之间的关系
2014/08/23 PHP
php opendir()列出目录下所有文件的实例代码
2016/10/02 PHP
php each 返回数组中当前的键值对并将数组指针向前移动一步实例
2016/11/22 PHP
微信公众平台开发教程③ PHP实现微信公众号支付功能图文详解
2019/04/10 PHP
Javascript 定时器调用传递参数的方法
2009/11/12 Javascript
jquery load()在firefox(火狐)下显示不正常的解决方法
2011/04/05 Javascript
window.location.reload()方法刷新页面弹出要再次显示该网页对话框
2013/04/24 Javascript
js计算文本框输入的字符数
2015/10/23 Javascript
原生javascript实现解析XML文档与字符串
2016/03/01 Javascript
深入浅析search 搜索框的写法
2016/08/02 Javascript
Javascript中判断一个值是否为undefined的方法详解
2016/09/28 Javascript
JavaScript登录验证码的实现
2016/10/27 Javascript
js实现textarea限制输入字数
2017/02/13 Javascript
jQuery在header中设置请求信息的方法
2017/03/06 Javascript
使用 NodeJS+Express 开发服务端的简单介绍
2017/04/07 NodeJs
jQuery 禁止表单用户名、密码自动填充功能
2017/10/30 jQuery
微信小程序图片轮播组件gallery slider使用方法详解
2018/01/31 Javascript
关于JavaScript数组去重的一些理解汇总
2020/09/10 Javascript
[56:35]DOTA2上海特级锦标赛C组小组赛#1 OG VS Archon第二局
2016/02/27 DOTA
在Python的Django框架中实现Hacker News的一些功能
2015/04/17 Python
以一个投票程序的实例来讲解Python的Django框架使用
2016/02/18 Python
运行django项目指定IP和端口的方法
2018/05/14 Python
pycharm 取消默认的右击运行unittest的方法
2018/11/29 Python
解决Python找不到ssl模块问题 No module named _ssl的方法
2019/04/29 Python
pandas 层次化索引的实现方法
2019/07/06 Python
CSS3旋转——彩色扇子兼容firefox浏览器
2013/06/04 HTML / CSS
英国豪华真皮和布艺沙发销售网站:Darlings of Chelsea
2018/01/05 全球购物
奥地利网上现代灯具和灯饰店:Lampenwelt.at
2018/01/29 全球购物
MCAKE蛋糕官方网站:一直都是巴黎的味道
2018/02/06 全球购物
简述安装Slackware Linux系统的过程
2012/01/12 面试题
《乡下孩子》教学反思
2014/04/17 职场文书
竞选班干部演讲稿
2014/04/24 职场文书
成立公司计划书
2014/05/07 职场文书
有子女的离婚协议书怎么写(范本)
2014/09/29 职场文书
2015年大学班长个人工作总结
2015/04/24 职场文书
2016年安全月活动总结
2016/04/06 职场文书