关于关闭页签时调用接口的解决方案

前言

在进行开发时遇到一个小问题,就是在用户关闭页签时如果某个状态是开启时那么就调用关闭接口。很简单的一个需求,它还能翻起什么浪花呢~

问题

onMounted(() => {
    // 添加 beforeunload 事件监听器
    window.addEventListener('beforeunload', handleCarBeforeUnload);
});

// 处理 beforeunload 事件
const handleCarBeforeUnload = (event) => {
    // 只有开启状态退出时执行关闭
    if (data.deviceStatus) {
        API.closeDevice();
    }
};
onBeforeUnmount(() => {
    // 添加 beforeunload 事件监听器
    window.removeEventListener('beforeunload', handleCarBeforeUnload);
});

失败!!

什么情况难道是没触发?,那就debugger下看看,结果是进入了, 那就奇怪了

那只能祭出无上 AI 大法了,于是得到了以下的结果

在 beforeunload 事件中使用 axios 调用接口可能会失败,原因主要有以下几点:

  • 1. 页面卸载时异步请求不可靠
    • 当页面开始卸载时,浏览器会逐步终止页面的 JavaScript 执行环境,包括未完成的异步请求(如 axios 的 HTTP 请求)。
    • axios 是基于 XMLHttpRequest 或 fetch 的异步请求库,而异步请求在页面卸载时可能被浏览器直接中断,导致请求无法完成。
  • 2. beforeunload 事件的限制
    • beforeunload 事件的主要目的是提示用户是否确认离开页面,而不是用于执行复杂的逻辑(如发送请求)。
    • 浏览器可能会限制 beforeunload 事件中的代码执行时间,以确保页面能够快速卸载。
  • 3. 解决方案:使用 navigator.sendBeacon
    • navigator.sendBeacon 是专门为页面卸载时发送数据设计的,它会在页面卸载时异步发送数据,且不会被浏览器中断。
    • 将 axios 替换为 navigator.sendBeacon,可以确保请求成功发送。
    • 示例代码:
window.addEventListener('beforeunload', handleCarBeforeUnload);

function handleCarBeforeUnload() {
    const data = JSON.stringify({ event: 'page_unload', time: Date.now() });
    navigator.sendBeacon('/your-endpoint', data);
}

失败!!

解决

使用navigator.sendBeacon()方法不行主要是此方法为post请求,而我需要调用的是一个get方法,那既然不能一步就尝试一下写个同步请求。

const xhr = new XMLHttpRequest();
xhr.open('GET', '/your-api, false);
xhr.send();

失败!! 我感觉主要是因为这个

浏览器可能会限制 beforeunload 事件中的代码执行时间,以确保页面能够快速卸载。

既然不行那真的是天要亡我吗?这点小问题就难住了,不行我得继续我的无上 AI 大法,于是就有了下面这个最终版的方法.

// 处理 beforeunload 事件
const handleCarBeforeUnload = (event) => {
    // 只有开启状态退出时执行关闭
    if (data.deviceStatus) {
        const params = new URLSearchParams({
            force: true,
            _t: Date.now() // 防止缓存
        });
        // 方案1:使用 Image 对象(最可靠)
        const img = new Image();
        img.src = `/your-api?${params}`;
    }
};