前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【兼容性】监听页面关闭发送请求

【兼容性】监听页面关闭发送请求

作者头像
神仙朱
发布2021-09-09 15:02:47
4.7K0
发布2021-09-09 15:02:47
举报
文章被收录于专栏:Vue源码 & 前端进阶体系

这个是前端兼容性系列内容

因为前端监控会在页面关闭的时候,发送一下日志,所以会涉及到监听页面关闭,之前我们只监听了一个beforeunload 来发送数据

但是我看了之后发现应该没有这么简单实现,前端总要写一些乱七八糟的兼容代码的啊!

于是就去研究了一下,好家伙,兼容性五花八门

我测试的终端包括

1、Windows PC (10),Chrome

2、iOS(14,13,12,11),Safari

3、Android (10,9),自带浏览器

4、HarmonyOS(1),自带浏览器

是的,我还测试了鸿蒙,华为 yyds!

下面就来详细说说,本文分为

1、页面关闭动作

2、页面关闭事件

3、测试结论

4、兼容做法

5、页面关闭发送请求

亲身多次实验,but 数据仅供参考

页面关闭动作

我仔细想了想所有会导致页面关闭的动作

1、页面刷新

2、跳转页面

3、关闭tab

4、关闭浏览器

所以如果我要监听页面关闭,那么我必须要都兼容这些动作。

我是怎么做这些动作的,关闭tab ,pc 的不用说了吧

移动端就是打开浏览器的窗口界面,然后关闭

关闭浏览器则是在任务管理界面,把 app 划出

页面关闭事件

页面关闭有哪些事件,我直接列出来

1、beforeunload

2、pagehide

3、unload

它们触发的顺序和列出来的一样,beforeunload->pagehide->unload

下面来看针对这些事件的兼容情况

测试结论

PC 端对于上面 四个动作,3个事件 都支持,移动端则表现不一

先综述一下

1、 iOS 压根就不支持 beforeunload,unload 根据 iOS 版本支持程度也较低

2、Android 只有刷新支持 beforeunload,而 unload 的话好一些,支持 刷新和关闭tab

3、HarmonyOs ,刷新和跳转 支持 beforeunload,unload 只有 刷新支持

具体数据如下

所以综上所述,beforeunload 和 unload 在移动端并不是十分可靠

而 iOS 开发文档也说明了,load 这类事件支持不好,最好使用 pagehide 事件

https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW5

经过测试,结论如下

pagehide 的确支持程度要好很多,不管是PC 还是移动端,但是终究没能全部覆盖,有点遗憾啊,难道要抛弃这部分了吗

转念想了想, visibilitychange会在页面可见或隐藏时触发,或许能解决掉一部分

测试了一下,如下

WC,完全对 iOS 不支持啊,但是可以看见的确解决了一部分问题,把上面 HarmonyOs 、Android 都支持了

所以现在就剩两种情况无法监听到页面关闭了

1、关闭 tab 时,iOS14 以下(iOS13、iOS12、iOS11,其他版本未测)

2、关闭浏览器时,iOS 全不支持

这两种情况也没有什么好的办法,但是考虑到在移动端关闭应用通常是App切到后台然后再上滑关闭

而 iOS 在把浏览器切后台的时候,可以触发 visibilitychange ,所以可以算是解决掉 关闭浏览器的问题

至于关闭 tab,我调查大概四五个人,很少有关闭tab 的习惯,所以也不算是什么大问题

兼容做法

所以现在如果我们要监听页面关闭,那么我们最好监听四种事件,这样可以最大程度兼容

使用一个变量去判定是否已经执行过 页面关闭相关的处理逻辑

只要执行就行,谁执行没有关系,大家排好队

代码语言:javascript
复制
let isEndSendOK = false;
function report() {
  if (isEndSendOK) {
    return;
  }
  isEndSendOK = true;
  fetch('xxxxxx');
}

// 监听多个事件,做同一个事情,用一个标志位确定是否做过
// 移动端普遍只支持 pagehide
window.addEventListener('beforeunload', report);
window.addEventListener('pagehide', report);
window.addEventListener('unload', report);

// IOS14 之前不会冒泡,只能监听document
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState !== 'visible') {
    report();
  } else {
    // 如果界面又显示了,说明没有关闭,重置标志位
    isEndSendOK = false;
  }
});

页面关闭发送请求

在页面关闭的时候发送请求,因为请求是异步的,所以大多数时候并不一定成功

使用同步的方式发送请求是可以,但是 会迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚。

会导致非常差的下一个页面载入性能,所以如果你的页面在体验发现加载慢,有可能是上一个页面的锅

上一个使用 同步方式发送 XHR 的例子

代码语言:javascript
复制
function xhrSync(type) {
  var request = new XMLHttpRequest();
  request.open(
    'POST',
    'https://ke.qq.com/report' ,
    false  //false表示同步请求
  );
  request.send('xhr');
}

并且现在有部分浏览器针对这个情况,已经不允许在页面关闭的时候发送同步请求了,不然就会报错

Chrome 在文档中也有相关的说明

https://www.chromestatus.com/feature/4664843055398912

Chrome now disallows synchronous XHR during page dismissal when the page is being navigated away from or closed by the user. This involves the following events (when fired on the path of page dismissal): beforeunload, unload, pagehide, and visibilitychange

反正就是不给你发!不然我报错!

反正异步发送不靠谱,同步又不好

异步到底有多步靠谱,我还做了个测试,看看异步和同步的支持情况,数据如下,仅供参考

表示能成功收到请求,

表示收不到请求,Error 表示报错

你可能会问,你关闭 tab 和 浏览器,你是怎么抓到请求的,因为我的页面用 whistle 代理,请求会经过 whistle,所以可以在界面上看到所有抓到的请求,不会的可以参考 前端调试必备-whistle 入门

你可能会问,你关闭 tab 和 浏览器,你是怎么抓到请求的

因为我的页面用 whistle 代理,请求会经过 whistle,所以可以在界面上看到所有抓到的请求

反正就是不行!

针对这个情况, navigator.sendBeacon() 方法就出现了

会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载

yyds!

但是好像支持情况也不太好嘛,IE 再见了

经过实测,只有 iOS11 没有sendBeacon 这个方法,其他的 HarmonyOS ,Android,PC、iOS11以上 都能成功发送请求

但是呢,诶,但是,哪里有这么完美的东西呢

sendBeacon 只支持发送少量数据,如果发送太大的数据,是会报错的

具体是返回一个false,表示该请求无法加入传输队列

我查的资料以及自己尝试,最大是 64KB,多1B 都不行

例子我是拷贝的

代码语言:javascript
复制
function testBeaconLimit() {
  var url = 'https://ke.qq.com/report';
  var n = 1024 * 64; 

  // this method courtesy of http://stackoverflow.com/questions/14343844/create-a-string-of-variable-length-filled-with-a-repeated-character
  var data = new Array(n + 1).join('X'); // generate string of length n

  if (!navigator.sendBeacon(url, data)) {
    alert('data limit reached');
  }
}

并且还有一个要注意的

虽然说是最大 64KB,但是频繁发送64KB 也有可能会错误,所以使用 sendBeacon 一定要做好错误兼容哦

至于这里发送请求的兼容做法的话

如果你不在乎性能,可以先使用 sendBeacon 发送,不支持或者报错,再使用同步的 XMLHttpRequest

但是因为同步的 xhr 可能会报错,也要做好错误处理

我搜到一个开源库的sendBeacon兼容处理

https://github.com/miguelmota/Navigator.sendBeacon/blob/master/sendbeacon.js

可以参考一下

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 神仙朱 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档