Web Beacon 刷新/关闭页面之前发送请求

简介: 背景:有一个任务非常耗时会消耗后台大量算力,所以在退出页面的时候,要求前端这边发送一个请求来杀死任务。一开始以为这个需求非常简单,就是在进入其他路由前,发送一下请求,杀死一下任务就好了。然而现实狠狠的打了我的脸,因为退出页面的场景不止切换路由~退出页面场景:还在本网站,跳到其他路由刷新页面/关闭页面也需要发送请求来杀死任务还在本网站,跳到其他路由这个比较简单,在Vue中可以通过路由离开的钩子beforeRouteLeave来实现: beforeRouteLeave(to, from, next) { if (任务运行中) { // 发送请求

image.png

背景:

有一个任务非常耗时会消耗后台大量算力,所以在退出页面的时候,要求前端这边发送一个请求来杀死任务

一开始以为这个需求非常简单,就是在进入其他路由前,发送一下请求,杀死一下任务就好了。

然而现实狠狠的打了我的脸,因为退出页面的场景不止切换路由~

退出页面场景:

  1. 还在本网站,跳到其他路由
  2. 刷新页面/关闭页面也需要发送请求来杀死任务

还在本网站,跳到其他路由

这个比较简单,在Vue中可以通过路由离开的钩子beforeRouteLeave来实现:

beforeRouteLeave(to, from, next) {
    if (任务运行中) {
        // 发送请求
    }else{
        next(true) // 用户离开
    }
 }

刷新页面/关闭页面的情况:

然而在刷新页面的时候,beforeRouteLeave并不会执行,接着想到了下面这两个API.

beforeunloadunload

beforeunload 当浏览器窗口关闭或者刷新时触发:

介绍

使用这个API可以阻止页面直接关闭,用户通过点击确定/取消按钮,来决定是否不关闭/刷新当前页面。

在 chrome 下长这个样子,你们肯定都见过:

image.png

如何使用

这个 API 的使用非常简单,只要在页面加载的时候监听一下此事件,在需要出现弹窗的时候return 一个可以转化为 true 的值,就可以了。

// 页面卸载之前
let killTask = false; // 是否杀死任务
window.onbeforeunload = e => {
  if (任务运行 && 对应页面) {
    killTask = true;
    return '您可能有数据没有保存'; // 在部分浏览器可以修改弹窗标题
  } else {
    killTask = false;
  }
  // 没有return一个可以转化为true的值 就不会出现弹窗
};

出现此弹窗的浏览器行为

以下行为是基于 chorme:

  1. 焦点:你没有点击取消/确定之前,焦点会一直在此弹窗上
  2. 你无法在出现弹窗的页面上执行任何操作
  3. 在其他页面也只能执行简单的点击操作,弹窗还是存在页面中间,无法使用键盘,
  4. 键盘:键盘被绑定在弹窗上,只能通过按键EscEnter来执行取消/确定操作
  5. 弹窗不是页面的 dom,是浏览器的行为
  6. 用户取消/确定,没有回调 API,无法得知

弹窗标题

chrome 中刷新页面的标题:重新加载此网站?

chrome 中关闭页面的标题:离开此网站?

现在大部分浏览器都不允许修改弹窗的标题,这个是为了安全考虑,来保证用户不受到错误信息的误导,

迷茫

一开始我以为既然可以拦截到用户的刷新/关闭页面的操作,出现了上面那个弹窗,这个需求就已经做完了的时候。

然后发现,浏览器竟然没有提供用户点击确定/取消刷新页面的回调

到这里我陷入了迷茫,盯着beforeunload这个 API 思考了起了人生的意义(其实是在发呆),盯着盯着,从beforeunloadbefore我也就想到了unload这个 API。

瞬间又燃起了斗志,何不试试这个unload

unload当页面正在被卸载的时候触发该事件

介绍

当页面正在被卸载的时候触发该事件,该事件不可取消,为不可逆操作。

使用

直接监听该事件就可以了。

window.onunload = e => {}

结合需求:

killTaskbeforeunload时定义的变量,每次进入回调,都会给killTask赋值,使用这个值就可以判断什么时候可以发送请求杀死任务。

window.onunload = e => {
  if (killTask && 对应页面) {
    // 发送请求
  }
};

到这里大家肯定以为已经做出来了该需求,事实上,并没有!

无法发送异步请求

我使用的是axios来发送请求,请求发出去了,但是被取消了,服务器那边根本没有收到请求,如下。

image.png

经过一顿分析:发现是axios请求是异步的问题,谷歌之后发现axios不支持同步的请求

最后使用原生的XMLHttpRequest对象,让请求同步

大功告成! 实际上,上面才是我第一次要发的内容,而下面更好的解决方法!

缺陷与更好的建议:

当我把这篇文章发布在公众号上,被奇舞周刊转载了,上面一些大佬给我提了一些建议。

研究了一下,结果...好吧!我承认我是菜鸡。

hey~ 不过这正是我写博客的收获之一,分享经验,收获知识!

性能缺陷:

XHR同步请求会阻碍页面卸载,如果是刷新/跳转页面的话,页面重新展示速度会变慢,导致性能问题

毕竟向网络发送请求并获得响应可能会超级慢,有可能是用户网络环境比较差,又或者是服务器挂了,请求一直没返回回来...

基于性能问题,大佬们推荐使用Beacon代替XHR,然后经过一番搜索...

Beacon API

  1. Beacon API用于将少量数据通过post请求发送到服务器
  2. Beacon是非阻塞请求,不需要响应

完美解决性能缺陷问题:

  1. 浏览器将 Beacon 请求排队让它在空闲的时候执行并立即返回控制
  2. 它在unload状态下也可以异步发送,不阻塞页面刷新/跳转等操作。

所以**Beacon可以完美解决上面提到的因XHR同步请求阻塞而引起的性能缺陷问题**。

使用:navigator.sendBeacon()

完整API

let result = navigator.sendBeacon(url, data);

Beacon是挂在navigator下面的,上面就是它的完整API。

result是一个布尔值,代表这次发送请求的结果:

  • 如果浏览器接受并且把请求排队了则返回 tru
  • 如果在这个过程中出现了问题就返回 false

navigator.sendBeacon接受两个参数:

  1. url: 请求的 URL。请求是 POST 请求。
  2. data: 要发送的数据。 数据类型可以是:ArrayBufferView, Blob,  FormData,Sting。

来看一个用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同步请求或者不做处理
}

web wroker中使用Beacon

因为Beacon是挂在navigator下面,而web worker也有navigator,去找了一下,真的给我找到了。

这儿有一个MDN提供的栗子,可以点进去看一下。

PS:对web worker不熟悉的同学可以看我这篇文章

Beacon其他相关

  • 客户端优化:可以将 Beacon 请求合并到其他请求上,一同处理, 尤其在移动环境下。
  • Beacon更多的情况是用于做前端埋点,监控用户活动,它的初衷也基于此。

小结

本文总共讲了三个API,beforeunloadunloadBeaconBeacon这个API估计知道的人比较少,以后遇到前端埋点和页面卸载前发送请求的需求,记得使用这三个API。


目录
相关文章
|
1月前
|
编解码 前端开发 JavaScript
使用 CSS 打印样式为 Web 页面设置专业的打印机效果
使用 CSS 打印样式为 Web 页面设置专业的打印机效果
52 2
|
21天前
|
前端开发 开发者
WEB自定义页面请求响应
Web组件支持在应用拦截到页面请求后自定义响应请求能力。开发者通过onInterceptRequest()接口来实现自定义资源请求响应 。自定义请求能力可以用于开发者自定义Web页面响应、自定义文件资源响应等场景。
22 0
|
2月前
|
SQL 存储 安全
Web安全-CSRF跨站请求伪造
Web安全-CSRF跨站请求伪造
82 5
|
2月前
|
存储
在 Web 中判断页面是不是刷新
【9月更文挑战第10天】在Web开发中,判断页面是否刷新有多种方法:1) 监听`popstate`事件,检测用户是否通过历史记录访问页面;2) 记录并比较页面加载时间戳,若相差极小,则可能为刷新;3) 利用本地存储设置特定值,若该值不存在或不符合预期,则页面可能被刷新。然而,这些方法并非绝对准确。
168 3
html,web页面朗读文字,朗读中文,朗读英文
html,web页面朗读文字,朗读中文,朗读英文
|
3月前
|
数据处理 开发者 UED
FastAPI 的模板引擎简直太神奇啦!这就是构建动态 Web 页面的终极秘籍,快来一探究竟!
【8月更文挑战第31天】FastAPI 是一款高性能异步 Web 框架,可通过集成模板引擎(如 Jinja2 或 Mako)实现动态页面渲染。使用模板引擎可分离页面结构与数据,简化代码并提升可维护性。此外,它还提供丰富的语法支持,如循环和条件判断,从而增强页面展示效果及开发效率。通过简单的配置步骤,即可在 FastAPI 中启用模板引擎,显著改善用户体验。
317 1
|
2月前
|
数据处理 Python
Django视图:构建动态Web页面的核心技术
Django视图:构建动态Web页面的核心技术
|
3月前
|
Web App开发 安全 JavaScript
【Azure 应用服务】App Service 通过配置web.config来添加请求返回的响应头(Response Header)
【Azure 应用服务】App Service 通过配置web.config来添加请求返回的响应头(Response Header)
|
3月前
|
缓存 运维 网络协议
一台新PC进行Web页面请求的历程:技术深度剖析
【8月更文挑战第24天】在当今数字化时代,当我们轻轻点击浏览器上的一个链接,背后其实经历了一场复杂而精妙的交互过程。本文将带您深入探索,从一台全新PC的角度出发,揭秘Web页面请求的全过程,展现这背后隐藏的技术奥秘。
33 0
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
101 3