编程 告别传统方法:在关闭浏览器标签前可靠发送 HTTP 请求

2025-08-15 15:16:12 +0800 CST views 740

告别传统方法:在关闭浏览器标签前可靠发送 HTTP 请求

在前端开发中,我们常常遇到这样一个需求:用户即将关闭页面或标签页时,需要向服务器发送最后一条数据。比如:

  • 页面停留时间统计
  • 日志或行为数据上传
  • 用户草稿自动保存

然而,看似简单的操作在实践中却充满挑战。


传统方法为何不可靠?

当用户关闭页面时,浏览器会触发 pagehideunload 等卸载事件。此时:

  • 标准的异步请求(fetch 或 XMLHttpRequest)可能会被浏览器中断
  • 页面卸载后,JavaScript 执行环境消失,请求可能直接取消

过去的解决方案是 同步 XMLHttpRequest

const xhr = new XMLHttpRequest();
xhr.open('POST', '/log', false); // false 表示同步请求
xhr.send(JSON.stringify({ event: 'close_tab' }));

❌ 缺点:

  • 阻塞主线程
  • 页面 UI 卡顿
  • 用户体验极差

sendBeacon() 是 W3C 专门为“页面卸载时发送数据”设计的 API:

window.addEventListener('pagehide', (event) => {
  if (event.persisted) return; // 页面进入 bfcache,不发送数据

  const analyticsData = {
    timeOnPage: Math.round(performance.now()),
    lastAction: 'close_tab',
  };

  const blob = new Blob([JSON.stringify(analyticsData)], {
    type: 'application/json; charset=UTF-8',
  });

  const success = navigator.sendBeacon('/log-analytics', blob);

  if (success) {
    console.log('分析日志已成功加入发送队列。');
  } else {
    console.error('无法发送分析日志。');
  }
});

✅ 特点:

  • 异步非阻塞:不影响页面关闭速度
  • 浏览器保证发送:即使页面已关闭
  • 限制:仅支持 POST 请求,不能自定义请求头

适用场景:日志、统计、行为数据上传等轻量级数据。


现代方案二:fetch({ keepalive: true })

对于需要更多灵活性(如 PUT 方法或自定义请求头)的场景,可以使用 fetch 的 keepalive 选项:

window.addEventListener('pagehide', (event) => {
  if (event.persisted) return;

  const draftContent = document.getElementById('editor').value;
  if (!draftContent) return;

  const draftData = {
    content: draftContent,
    timestamp: Date.now(),
  };

  try {
    fetch('/api/drafts/save', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(draftData),
      keepalive: true, // 关键!
    });
    console.log('保存草稿的请求已提交。');
  } catch (e) {
    console.error('提交保存草稿请求时发生错误:', e);
  }
});

✅ 特点:

  • 支持多种 HTTP 方法(POST/PUT 等)
  • 允许设置请求头
  • 异步非阻塞,保证用户体验
  • 与 sendBeacon 类似,页面关闭后请求仍会继续

如何选择?

需求类型推荐方案
日志/统计/行为数据上传navigator.sendBeacon()
更新资源/PUT请求/自定义头fetch({ keepalive: true })

总结:

  • 传统同步请求阻塞主线程,用户体验差
  • sendBeaconfetch({ keepalive: true }) 可以在 不阻塞用户体验 的前提下,可靠地发送最后一条数据

推荐文章

404错误页面的HTML代码
2024-11-19 06:55:51 +0800 CST
JavaScript设计模式:组合模式
2024-11-18 11:14:46 +0800 CST
介绍 Vue 3 中的新的 `emits` 选项
2024-11-17 04:45:50 +0800 CST
免费常用API接口分享
2024-11-19 09:25:07 +0800 CST
FcDesigner:低代码表单设计平台
2024-11-19 03:50:18 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
Linux 常用进程命令介绍
2024-11-19 05:06:44 +0800 CST
OpenCV 检测与跟踪移动物体
2024-11-18 15:27:01 +0800 CST
Golang 随机公平库 satmihir/fair
2024-11-19 03:28:37 +0800 CST
程序员茄子在线接单