监听页面关闭

| 监听页面关闭的方法

涉及场景:浮窗/左上角关闭/物理法返回键/右滑/切换应用/回到桌面

终极方案:同时使用 visibilitychange 和 pagehide。

mounted(){
  window.addEventListener('pagehide', (e) => this.test1()); // 用于兼容 IOS 关闭页面
  document.addEventListener('visibilitychange', (e) => this.test2()); // 浮窗/切换应用等隐藏页面
}
destroyed() {
  window.removeEventListener('pagehide', e => this.test1())
  document.removeEventListener('visibilitychange', e => this.test2())
},
methods: {
  test1(){ // pagehide
    if(IOS){
      navigator.sendBeacon("/common/IP/getIP");
    }
  },
  test2(){ // visibilitychange
    // 用户息屏、或者切到后台运行 (离开页面)
    if (document.visibilityState === 'hidden') {
      navigator.sendBeacon("/common/IP/getIP");
    }
  }
},

遇到的坑:

  1. 监听 beforeunload PC 中有效(且可异步请求),微信中无效。

beforeunload & unload

beforeunload - 当浏览器窗口关闭或者刷新时,会触发 beforeunload 事件

unload - 当文档或一个子资源正在被卸载时, 触发 unload 事件,在 beforeunload 和 pagehide 事件后触发

unload 文档处于以下状态:

  • 所有资源仍存在 (图片, iframe 等.)
  • 对于终端用户所有资源均不可见
  • 界面交互无效 (window.open, alert, confirm 等.)
  • 错误不会停止卸载文档的过程
  • 请注意 unload 事件也遵循文档树:父 iframe 会在子 iframe 卸载前卸载
  1. visibilitychange 在 android 中全场景生效,在 IOS 中关闭页面不生效,需同时使用 pagehide。pagehide[android/IOS] 仅限于关闭页面,对于切换应用/浮窗/回到桌面场景不生效。需注意 visibilitychange - document,pagehide - window
// 用户息屏、或者切到后台运行 (离开页面)
if (document.visibilityState === 'hidden') {
  console.log('hidden');
}

// 用户打开或回到页面
if (document.visibilityState === 'visible') {
  console.log('页面可见');
}
  1. 使用 axios 异步请求接口,由于页面卸载会导致请求发送取消或者发送失败。所以需要将请求改为同步。 将 xhr 请求改为同步,虽然能够完成发送数据,但存在以下两个问题:

    • 部分浏览器已经不支持同步的 XMLHttpRequest 对象了(即 open()方法的第三个参数为 false);
    • xhr 请求改为同步后,会阻塞页面的卸载和跳转,导致下一个页面导航加载的时机变晚,用户体验较差。

为了解决这个问题,浏览器引入了 Navigator.sendBeacon()方法。这个方法还是异步发出请求,但是请求与当前页面脱钩,作为浏览器的任务,因此可以保证会把数据发出去,不拖延卸载流程。