You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

页面unload事件触发时请求未发送,使用navigator.sendBeacon也无效

问题核心原因
  • 异步请求在页面卸载场景会被强制终止:unload 事件触发时浏览器会立即销毁页面上下文,所有 pending 状态的异步XHR/fetch请求都会被直接终止,你原逻辑中用的await callEndpoint属于异步请求,还没完成发送就被中断,自然无法到达服务端。
  • useEffect清理逻辑错误:你将removeEventListener写在forEach的回调内部,这个return是forEach回调的返回值,不会被useEffect识别为全局清理函数,会导致事件监听器泄漏、重复注册的问题。
  • sendBeacon调用姿势错误:sendBeacon仅支持POST请求,参数仅支持FormData、Blob、字符串三种类型,不能直接传入普通JSON对象,且默认请求头不为application/json,如果服务端对请求头/格式有校验就会接收失败。
修复方案

1. 修正事件监听器注册逻辑

首先修复useEffect的事件绑定、清理逻辑,避免闭包和监听器泄漏问题:

useEffect(() => {
  if (!events) return;
  // 收集所有监听器的清理函数,统一执行
  const cleanupList = [];

  events.forEach((event) => {
    const handler = () => {
      // 单独处理页面卸载类事件
      if (['unload', 'beforeunload', 'pagehide'].includes(event.type)) {
        // 构造业务请求参数
        const requestData = JSON.stringify({
          // 填写你的业务请求参数
        });
        // 封装为Blob,设置JSON格式的请求头,兼容服务端参数校验
        const blobData = new Blob([requestData], { type: 'application/json' });
        const fullUrl = `${api}${event.listener}`;
        // sendBeacon由浏览器保证在页面卸载前完成发送,无需等待返回
        const sendSuccess = navigator.sendBeacon(fullUrl, blobData);
        // 如果sendBeacon发送失败(比如超过64KB大小限制),降级用fetch+keepalive
        if (!sendSuccess) {
          fetch(fullUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              // 其他自定义请求头
            },
            body: requestData,
            keepalive: true // 告诉浏览器在页面卸载后继续完成请求
          });
        }
        return;
      }
      // 普通事件保留原逻辑
      handleEvent(api, event);
    };

    window.addEventListener(event.type, handler, event.options);
    // 存入清理函数列表
    cleanupList.push(() => window.removeEventListener(event.type, handler, event.options));
  });

  // 全局清理函数
  return () => cleanupList.forEach(clean => clean());
// 补充完整依赖,不要随意关闭eslint校验,避免闭包问题
}, [events, api]);

2. 额外优化建议

  • 优先使用pagehide事件代替unload:W3C已经不推荐使用unload事件,移动端Safari等浏览器在很多场景不会触发unload,pagehide的兼容性和可靠性更高,你可以直接修改事件数组中的type字段为pagehide。
  • 不要在卸载类事件中执行任何异步逻辑、DOM操作、弹窗操作,这些行为都会被浏览器强制拦截。

内容的提问来源于stack exchange,提问作者Yumie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 22:45:02