如何确保用户关闭网页时,关键数据100%发送到服务器?

更新日期: 2025-07-19阅读: 39标签: 请求

用户关闭浏览器标签的瞬间,往往是数据上报的关键时刻。比如:

  • 电商平台需要记录用户离开前的最后浏览商品

  • 在线文档工具要自动保存未提交的草稿

  • 数据分析需捕获页面真实停留时长

但传统技术方案会失败:当页面卸载时,普通网络请求会被浏览器强制取消。下面通过真实案例,解析两种100%可靠的解决方案。


一、为什么普通请求会失败?

当用户关闭标签页,浏览器触发两个关键事件:

  1. pagehide(页面隐藏)

  2. unload(页面卸载)

此时若用fetch或XMLHttpRequest发送请求:

// 失败案例
window.addEventListener('unload', () => {
  fetch('/save-data') // 请求将被浏览器中断
});

根本原因:页面JS执行环境已被销毁,浏览器会主动终止未完成的请求。


二、过时的"暴力解法"及其危害

早期开发者使用同步XMLHttpRequest

// 已淘汰的方案!
const xhr = new XMLHttpRequest();
xhr.open('POST', '/save', false); // 第三个参数false表示同步
xhr.send(data);

致命缺陷

  • 完全阻塞主线程,导致页面卡死

  • 用户无法切换标签页,甚至无法关闭浏览器

  • 移动端可能触发系统警告

据统计,同步请求会使页面响应延迟300ms以上,用户体验极差。


三、现代方案一:navigator.sendBeacon()

这是浏览器专为"最后一刻"数据发送设计的api

核心优势

  • 不阻塞页面关闭

  • 浏览器保证请求发送

  • 使用简单

代码示例

window.addEventListener('pagehide', (event) => {
  // 检查是否进入bfcache(页面未真正关闭)
  if (event.persisted) return; 
  
  const logData = {
    duration: performance.now(),
    page: location.href
  };
  
  // 转换成Blob格式
  const blob = new Blob(
    [JSON.stringify(logData)], 
    { type: 'application/json' }
  );
  
  // 发送请求(无需等待响应)
  navigator.sendBeacon('/log', blob);
});

注意事项

特性限制说明
请求方法仅支持POST
请求头不可自定义
数据大小部分浏览器限制在64KB以内
响应处理无法获取服务器返回结果


四、现代方案二:Fetch API + keepalive

适合需要更多控制的场景:

window.addEventListener('pagehide', async (event) => {
  if (event.persisted) return;
  
  const draft = editor.getValue(); // 获取编辑器内容
  
  // 使用keepalive模式
  fetch('/api/save-draft', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ content: draft }),
    keepalive: true // 关键参数!
  });
});

与sendBeacon对比

能力sendBeaconfetch+keepalive
支持GET请求
自定义Header
发送FormData
跨域凭证携带

五、如何选择最佳方案?

根据业务需求决策:

  • 选 sendBeacon
    只需发送简单日志(如点击追踪/页面停留统计)

  • 选 fetch+keepalive
    需要保存用户数据(如文档草稿)、必须携带认证信息、需使用PUT/DELETE方法


六、真实场景中的避坑指南

  1. 事件监听优先用 pagehide
    unload 事件可能阻止浏览器使用往返缓存(bfcache),影响用户体验

  2. 重要数据需要双重保障

    // 提前在visibilitychange事件中发送
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden') {
        navigator.sendBeacon('/backup-data');
      }
    });
  3. 服务器端必须幂等处理
    因网络波动可能造成重复发送,服务端应做去重处理


七、特殊场景解决方案

场景1:需要发送超大数据
拆分数据 + 多次发送:

// 分段发送日志
const chunks = splitLargeData(data); 
chunks.forEach(chunk => {
  navigator.sendBeacon('/log-chunk', chunk);
});

场景2:低版本浏览器兼容

if (!navigator.sendBeacon) {
  // 回退方案:使用同步XHR(仅作保底)
  const xhr = new XMLHttpRequest();
  xhr.open('POST', url, false);
  xhr.send(data);
}


结语

通过sendBeacon和fetch+keepalive,开发者能在不影响用户体验的前提下,实现关闭页面的数据可靠上报。关键点在于:

  1. 优先选用浏览器原生方案

  2. 根据数据类型选择合适API

  3. 服务端做好数据幂等处理

最新统计显示,全球98%的浏览器已支持这两种方案,可放心用于生产环境。

实际部署前,建议使用Chrome DevTools的Application > Background services模块测试请求发送状态,确保万无一失。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/12876

nginx 301跳转https后post请求失效问题解决

强制把http请求跳转到https,结果发现App有部分的功能不能使用,因为App一共设置了4种请求方式,分别是GET,POST,DELETE和OPTIONS方式,设置301跳转后所有的请求方法都变成了GET方式,导致一些功能无法正常使用.

HTTP请求的11个处理阶段

几乎所以有关Nginx书只要是讲深入点的就会讲到Nginx请求的11个处理阶段,要记住这些真是不易,人脑特别不擅长记住各种东西,只能做些索引罢了,能做到知道这个知识点在哪儿能找到不就行了,可是你去面试还是问这些理论,所以这里汇总下记录如下

http请求过程的7个步骤

HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:建立TCP连接、Web浏览器向Web服务器发送请求命令、Web浏览器发送请求头信息、 Web服务器应答

http请求的几种类型

http请求中的8种请求方法:opions 返回服务器针对特定资源所支持的HTML请求方法 ,Get 向特定资源发出请求,Post 向指定资源提交数据进行处理请求

nodejs http请求相关总结

通过node提供的http模块,可以通过其提供的get()和request()两个方法发起http请求,get()是对request()方法的封装,方便发起get请求,如果要实现post请求,那么需要对request()方法进行封装。

ajax异步请求302分析

遇到这样一种情况,打开网页两个窗口a,b(都是已经登录授权的),在a页面中退出登录,然后在b页面执行增删改查,这个时候因为授权原因,b页面后端的请求肯定出现异常(对这个异常的处理,进行内部跳转处理),b页面中的ajax请求的回调中就会出现问题

POST 请求的三种常见数据提交格式

本文所讲的 POST 请求是 HTTP/1.1 协议中规定的众多 HTTP 请求方法的其中最常用的一个。一般使用 POST 请求方法向服务器发送数据(主要是一些创建更新操作),本文讨论的是 POST 请求方法常用的四种数据提交格式。

flutter之网络请求dio封装,拦截器的封装

flutter一直很火的网络请求插件dio,直接上代码,写成一个类,可以直接使用,包含请求的封装,拦截器的封装

HTTP请求过程

当我们在web浏览器的地址栏中输入: www.baidu.com,然后回车,到底发生了什么?DNS域名解析采用的是递归查询的方式,过程是,先去找DNS缓存->缓存找不到就去找根域名服务器->根域名又会去找下一级

nginx是怎么处理http请求的?

nginx首先决定要用配置文件里的哪个server{}块来处理,假设有下面的server{}配置;nginx会根据过来的http请求头里的Host字段里的值,来判断使用哪个server{}。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!