当前位置: 首页 > Web前端 > HTML5

sendBeacon在刷新-关闭页面前发送一个请求

时间:2023-04-05 01:09:33 HTML5

后台:后台有一个非常耗时耗算力的任务,所以在退出页面的时候,需要前端发送终止任务的请求。一开始觉得这个需求很简单,发送一个请求,然后杀掉任务,再进入其他路由即可。然而现实却狠狠地打击了我,因为退出页面的场景不仅仅是切换路由~退出页面场景:还是在这个网站,跳转到其他路由刷新页面/关闭页面也需要发送请求杀任务还是在这个网站,跳转到其他路由比较简单。在Vue中,可以通过路由离开的hookbeforeRouteLeave来实现:beforeRouteLeave(to,from,next){if(任务正在运行){//发送请求}else{next(true)//用户离开}}刷新/关闭页面:但是在刷新页面时,不会执行beforeRouteLeave,然后在关闭或刷新浏览器窗口时触发以下两个API.beforeunload和unloadbeforeunload:简介:使用该API可以阻止页面直接关闭,用户可以通过点击确定/取消按钮来决定是否关闭/刷新当前页面。chrome下是这样的,你肯定见过:如何使用这个API,使用起来很简单,只要在页面加载的时候监听这个事件,在弹出的时候返回一个可以转换为true的值窗口需要出现,然后确定。//在页面卸载之前让killTask??=false;//是否杀掉任务window.onbeforeunload=e=>{if(任务运行&&对应页面){killTask??=true;return'您可能有未保存的数据';//在某些浏览器中可以修改弹窗的标题}else{killTask??=false;}//如果没有可以转换为true的返回值,则不会出现弹窗};带有此弹出窗口的浏览器的行为:以下行为基于chorme:焦点:在您单击取消/确定之前,焦点将始终在此弹出窗口上。出现弹窗的页面不能进行任何操作,其他页面只能进行简单的点击操作。弹窗仍然存在于页面中间,无法使用键盘,键盘:弹窗绑定了键盘,取消/确认操作只能按Esc和Enter知道标题弹出窗口的标题:chrome中刷新页面的标题:重新加载此网站?chrome关闭页面的标题:离开这个网站?大多数浏览器不允许修改弹出窗口的标题。用户不被错误信息误导而一头雾水:一开始我以为既然可以拦截用户的刷新/关闭页面操作,出现上面的弹窗,那么这个需求就已经完成了。然后发现浏览器没有提供用户点击确定/取消刷新页面的回调。至此,我陷入了迷茫,盯着beforeunloadAPI思考人生的意义(其实是发呆),盯着看,从beforeunload之前,我也想到了unloadAPI。斗志瞬间又被点燃了,何不试试这一卸?unload当页面被卸载时触发该事件简介当页面被卸载时触发该事件。此事件无法取消,且为不可逆操作。直接听事件就行了。window.onunload=e=>{}结合要求:killTask??是beforeunload时定义的一个变量,每次进入回调都会给killTask??赋值,通过这个值来决定什么时候可以发送请求到杀死任务。window.onunload=e=>{if(killTask??&&对应页面){//发送请求}};大家一定以为这里提出了需求,其实并没有!无法发送异步请求。我使用axios发送请求。请求已发送,但已被取消。服务器根本没有收到请求,如下。分析了一下:发现axios请求是异步的。后来谷歌发现axios不支持同步请求。最后,使用本机XMLHttpRequest对象使请求同步,您就完成了!其实上面是我第一次想发的,下面是更好的解决方法!不足之处和更好的建议:我在公众号发表这篇文章时,被奇物周刊转载,上面的一些大佬给了我一些建议。研究了一下,结果……好吧!我承认我是菜鸟。哎~不过这是我写博客的收获之一,分享经验,增长知识!性能缺陷:XHR同步请求会阻碍页面卸载。如果页面刷新/跳转,页面重新显示速度会变慢,导致性能问题。毕竟,向网络发送请求并获得响应可能非常慢。可能是用户网络环境差,或者服务器宕机,请求没有返回……基于性能问题,大佬们推荐使用Beacon,而不是XHR。然后经过一些搜索...BeaconAPIBeaconAPI用于通过post请求向服务器发送少量数据。Beacon是非阻塞请求,不需要响应,完美解决性能缺陷问题:浏览器在空闲时将Beacon请求排队执行,并立即返回控制。也可以在unload状态异步发送,不阻塞页面刷新/跳转等操作。所以Beacon可以完美解决上述XHR同步请求阻塞带来的性能缺陷问题。使用:navigator.sendBeacon()完整的API:letresult=navigator.sendBeacon(url,data);Beacon挂在导航器下方,上面是它完整的API。result是一个布尔值,表示本次请求的结果:如果浏览器接受并排队请求,则返回tru;如果过程中出现问题,会返回falsenavigator。sendBeacon接受两个参数:url:请求的URL。该请求是一个POST请求。数据:要发送的数据。数据类型可以是:ArrayBufferView、Blob、FormData、Sting。看一个使用FormData传输数据的栗子,你就会明白://新建一个FormData,并添加一个键值对letdata=newFormData();data.append('hello','world');letresult=navigator.sendBeacon('./src',data);if(result){console.log('请求已成功排队等待执行');}else{console.log('失败');}浏览器支持:浏览器支持:Edge:14+,Firefox:31+,Chrome:39+,Opera:26+,IE:不支持。虽然现在浏览器对sendBeacon的支持已经很好了,但是我们还是有必要对其做一些兼容处理:在webworker中使用是因为Beacon挂在navigator下面,webworker也有navigator。我搜索了它并为我找到了它。这里举个MDN提供的栗子),大家可以点进去看看。PS:对webworkers不熟悉的同学可以看我的文章Beacon相关客户端优化:Beacon请求可以和其他请求合并在一起处理,尤其是在移动环境下。Beacon更多的是用于前端埋点和监控用户活动,其初衷也是基于此。小结本文一共讲了三个API,beforeunload、unload、Beacon。BeaconAPI估计很少有人知道。以后如果遇到前端埋点、页面卸载前发送请求的需求,记得使用这三个API。以上2019.02.19博客,前端积累文档,公众号,GitHub参考资料:MDN页面跳转时,统计数据丢失问题讨论。使用WebBeaconAPI记录活动