有一个任务很是耗时会消耗后台大量算力,因此在退出页面的时候,要求前端这边发送一个请求来杀死任务。前端
一开始觉得这个需求很是简单,就是在进入其余路由前,发送一下请求,杀死一下任务就行了。ios
然而现实狠狠的打了个人脸,由于退出页面的场景不止切换路由~git
这个比较简单,在Vue
中能够经过路由离开的钩子beforeRouteLeave
来实现:github
beforeRouteLeave(to, from, next) {
if (任务运行中) {
// 发送请求
}else{
next(true) // 用户离开
}
}
复制代码
刷新页面/关闭页面的状况:web
然而在刷新页面的时候,beforeRouteLeave
并不会执行,接着想到了下面这两个API
.chrome
beforeunload
和unload
介绍:axios
使用这个API
能够阻止页面直接关闭,用户经过点击肯定/取消按钮,来决定是否不关闭/刷新当前页面。api
在 chrome 下长这个样子,大家确定都见过:浏览器
如何使用安全
这个 API 的使用很是简单,只要在页面加载的时候监听一下此事件,在须要出现弹窗的时候return 一个能够转化为 true 的值,就能够了。
// 页面卸载以前
let killTask = false; // 是否杀死任务
window.onbeforeunload = e => {
if (任务运行 && 对应页面) {
killTask = true;
return '您可能有数据没有保存'; // 在部分浏览器能够修改弹窗标题
} else {
killTask = false;
}
// 没有return一个能够转化为true的值 就不会出现弹窗
};
复制代码
出现此弹窗的浏览器行为:
如下行为是基于 chorme:
焦点:你没有点击取消/肯定以前,焦点会一直在此弹窗上
你没法在出现弹窗的页面上执行任何操做
在其余页面也只能执行简单的点击操做,弹窗仍是存在页面中间,没法使用键盘,
键盘:键盘被绑定在弹窗上,只能经过按键Esc
、Enter
来执行取消/肯定操做
弹窗不是页面的 dom,是浏览器的行为
用户取消/肯定,没有回调 API,没法得知
弹窗标题:
chrome 中刷新页面的标题:从新加载此网站?
chrome 中关闭页面的标题:离开此网站?
如今大部分浏览器都不容许修改弹窗的标题,这个是为了安全考虑,来保证用户不受到错误信息的误导,
迷茫:
一开始我觉得既然能够拦截到用户的刷新/关闭页面的操做,出现了上面那个弹窗,这个需求就已经作完了的时候。
而后发现,浏览器居然没有提供用户点击肯定/取消刷新页面的回调。
到这里我陷入了迷茫,盯着beforeunload
这个 API 思考了起了人生的意义(实际上是在发呆),盯着盯着,从beforeunload
的before
我也就想到了unload
这个 API。
瞬间又燃起了斗志,何不试试这个unload
?
unload
当页面正在被卸载的时候触发该事件介绍
当页面正在被卸载的时候触发该事件,该事件不可取消,为不可逆操做。
使用
直接监听该事件就能够了。
window.onunload = e => {}
复制代码
结合需求:
killTask
为beforeunload
时定义的变量,每次进入回调,都会给killTask
赋值,使用这个值就能够判断何时能够发送请求杀死任务。
window.onunload = e => {
if (killTask && 对应页面) {
// 发送请求
}
};
复制代码
到这里你们确定觉得已经作出来了该需求,事实上,并无!
没法发送异步请求
我使用的是axios
来发送请求,请求发出去了,可是被取消了,服务器那边根本没有收到请求,以下。
通过一顿分析:发现是axios
请求是异步的问题,谷歌以后发现axios不支持同步的请求
最后使用原生的XMLHttpRequest对象,让请求同步
大功告成! 实际上,上面才是我第一次要发的内容,而下面更好的解决方法!
当我把这篇文章发布在公众号上,被奇舞周刊转载了,上面一些大佬给我提了一些建议。
研究了一下,结果...好吧!我认可我是菜鸡。
hey~ 不过这正是我写博客的收获之一,分享经验,收获知识!
XHR同步请求会阻碍页面卸载,若是是刷新/跳转页面的话,页面从新展现速度会变慢,致使性能问题。
毕竟向网络发送请求并得到响应可能会超级慢,有多是用户网络环境比较差,又或者是服务器挂了,请求一直没返回回来...
基于性能问题,大佬们推荐使用Beacon代替XHR,而后通过一番搜索...
Beacon
是非阻塞请求,不须要响应Beacon
请求排队让它在空闲的时候执行并当即返回控制unload
状态下也能够异步发送,不阻塞页面刷新/跳转等操做。因此**Beacon
能够完美解决上面提到的因XHR同步请求阻塞而引发的性能缺陷问题**。
navigator.sendBeacon()
完整API:
let result = navigator.sendBeacon(url, data);
复制代码
Beacon
是挂在navigator
下面的,上面就是它的完整API。
result
是一个布尔值,表明此次发送请求的结果:
navigator.sendBeacon
接受两个参数:
来看一个用FormData
来传递数据的栗子,你就懂了:
// 建立一个新的 FormData 并添加一个键值对
let data = new FormData();
data.append('hello', 'world');
let result = navigator.sendBeacon('./src', data);
if (result) {
console.log('请求成功排队 等待执行');
} else {
console.log('失败');
}
复制代码
浏览器支持:Edge:14+,Firefox:31+,Chrome:39+,Opera:26+,IE:不支持。
虽然如今浏览器对sendBeacon
的支持很好,咱们对其作一下兼容性处理也是有必要的:
if (navigator.sendBeacon) {
// Beacon 代码
} else {
// 回退到 XHR同步请求或者不作处理
}
复制代码
由于Beacon
是挂在navigator
下面,而web worker也有navigator
,去找了一下,真的给我找到了。
这儿有一个MDN提供的栗子,能够点进去看一下。
PS:对web worker不熟悉的同窗能够看我这篇文章
本文总共讲了三个API,beforeunload
、unload
和Beacon
,Beacon
这个API估计知道的人比较少,之后遇到前端埋点和页面卸载前发送请求的需求,记得使用这三个API。
以上2019.02.19
博客、前端积累文档、公众号、GitHub、wx:OBkoro一、邮箱:obkoro1@foxmail.com
参考资料: