当前位置: 首页 > 科技观察

如何在页面关闭或重定向时优雅地发送Ajax请求

时间:2023-03-20 00:03:50 科技观察

有时候我们需要做一些报表记录用户行为或者埋点,或者在用户离开页面时向服务器发送Ajax请求。如何保证请求能够正确投递是一个非常关键的点。接下来介绍一下如何操作:首先,做事件监听,浏览器有两个事件可以用来监听页面关闭,beforeunload和unload。beforeunload在文档和资源即将关闭时调用。这个时候文档还是可见的,关闭的事件还是可以取消的。比如下面的写法,会导致用户在刷新或关闭页面时有弹窗提醒用户是否关闭。window.addEventListener("beforeunload",function(event){//Canceltheeventasstatedbythestandard.event.preventDefault();//Chrome要求returnValuetobeset.event.returnValue='';});unload发生在页面已经被卸载的时候,这个文件的状态是:1.所有资源仍然存在(图片,iframe等);2、所有资源对用户不可见;3、界面交互无效(window.open、alert、confirm等);4.错误不会停止卸载文件的过程。基于以上两种方法,可以实现页面关闭的事件监听。为了安全起见,可以监视这两个事件。然后处理监听函数,让close事件只调用一次,比如用变量来控制发送请求的次数。其次,发送请求有上面的监听,事情只完成了一半。如果我们在监听的时候直接发送ajax请求,会发现请求被浏览器中止了,发送不出去。当页面卸载时,浏览器无法保证异步请求能够发送成功。我们有几种方法来解决这个问题:选项1:发送同步ajax请求varoAjax=newXMLHttpRequest();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){vardata=JSON.parse(oAjax.responseText);}else{console.log(oAjax);}};oAjax.send('a=1&b=2');这种方法虽然有效,但是用户需要等待请求结束才能关闭页面。糟糕的用户体验。方案二:发送异步请求,忽略服务端的ajaxabort。虽然异步请求会被浏览器中止,但是如果服务端可以忽略中止,仍然正常执行也是可以的。例如,PHP有ignore_user_abort函数来忽略中止。这需要修改背景,一般是不可行的。解决方案三:使用navigator.sendBeacon发送异步请求根据MDN:该方法主要用于满足统计和诊断代码的需要,通常在卸载(unload)文档之前尝试向web服务器发送数据。过早发送数据会导致错失收集数据的机会。但是,开发人员很难确保在文档卸载期间发送数据。因为用户代理通常会忽略在卸载事件处理程序中发出的异步XMLHttpRequest。从介绍中可以看出,该方法用于在用户离开时发送请求。非常适合这种场景。用法如下:navigator.sendBeacon(url[,data]);sendBeacon发送的数据可以是ArrayBufferView、Blob、DOMString或FormData。下面介绍几种使用sendBeacon发送请求的方式。可以修改header和content的格式,因为与服务器的通信方式一般是固定的。如果头部或内容被修改,服务器将无法正常识别。(1)使用Blob发送使用blob发送的好处是可以自己定义内容的格式和header。例如,在下面的设置方法中,可以将content-type设置为application/x-www-form-urlencoded。blob=newBlob([`room_id=123`],{type:'application/x-www-form-urlencoded'});navigator.sendBeacon("/cgi-bin/leave_room",blob);(2)使用FormData对象,但内容类型将设置为“multipart/form-data”。varfd=newFormData();fd.append('room_id',123);navigator.sendBeacon("/cgi-bin/leave_room",fd);(3)data也可以使用URLSearchParams对象,content-type会设置为"text/plain;charset=UTF-8"。varparams=newURLSearchParams({room_id:123})navigator.sendBeacon("/cgi-bin/leave_room",params);通过实验可以发现使用blob发送更方便,内容设置也更灵活。如果发送的消息抓包后,发现后台不识别。您可以尝试修改内容的字符串或标头,以找到合适的发送请求的方式。