关闭、刷新页面时发送请求的方式和时机

339 阅读2分钟

关闭、刷新页面时发送请求的方式

关闭、刷新时,即页面卸载时,正常的ajax请求都会被取消掉,所以需要特殊的方式,才能保证请求成功发送。

  1. sendbeacon

    image.png

  2. fetch 设置keepalive为true image.png

  3. 同步的xhr请求 xhrReq.open(method, url, false);

有人已经实现了对应的封装:beforeunload-request,可以查看下源码:大致方式就是按上述三种方式以及顺序依次尝试。

NOTE: 在使用beforeonloadRequest函数时,应该自行保证contentType的类型和数据的类型保持一致,参考Content-Type详解,比如

  1. contentTypeapplication/json时,数据对象应该使用JSON.stringify()处理;
  2. contentTypeapplication/x-www-form-urlencoded时,数据应改为 a=1&b=2&c%5Ba%5D=1&c%5Bb%5D=2格式,可用Qs.stringify()处理。

NOTE: 实测过程中,ios设备下关闭页面和刷新页面时,前端打印显示sendbeacon成功发送,但是就是后端就是没有接收日志。所以在上述情况下,本文的方案有问题。

关闭、刷新页面发送请求的时机

不同浏览器、不同系统、不同终端(pc、移动),对于页面生命周期的实现、页面卸载时触发的事件都不一样sendbeacon里有提到相关问题。想要精准的覆盖所有情况很有难度,所以监听了关闭、刷新页面时所有可能触发的事件,然后加上锁,防止重复上报。

// 防止重复上报
let isTriggerUnloadReport = false

window.onbeforeunload = () => {
    unloadReport()
};
window.onpagehide = () => {
    unloadReport()
};
window.onunload = () => {
    unloadReport()
};

// NOTE:当然在各种页面不可见得情况下也会触发,比如浏览器窗口被完全覆盖、当前浏览器tab页被切换、mac切窗口
document.onvisibilitychange = () => {
    if (document.visibilityState === 'hidden') {
        unloadReport()
    } else {
        isTriggerUnloadReport = false;
    }
};


function unloadReport () {
  if (isTriggerUnloadReport) {
    return;
  }
  isTriggerUnloadReport = true;

  beforeonloadRequest(`/report`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json; charset=UTF-8',
    },
    body: JSON.stringify({id: 1}),
  });
};