离开页面前触发消息推送

2022-06-25  本文已影响0人  alue

任务: 用户离开当前页面时,发送相关数据给服务器。

直观做法

    <a href="/some-other-page" id="link">Go to Page</a>
    
    <script>
    document.getElementById('link').addEventListener('click', (e) => {
      fetch("/log", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        }, 
        body: JSON.stringify({
          some: "data"
        })
      });
    });
    </script>

监听用户页面跳转按钮,然后绑定点击事件,完成POST请求。

这样行不通,原因有两点:

  1. 用户离开页面的方式未必是点击跳转,也可能是直接关闭页面或者输入其它网址。
  2. 即使是按预想点击跳转,这个POST请求也未必能发送出去。

为什么POST请求发不出去

上面这个POST请求,属于异步请求。也就是说,请求不会立即执行,而是先放置在任务队列中。
为了优化性能,浏览器设计了web页面生命周期。在跳转至别的页面过程中,页面会历经多个阶段。当进入 Terminated 阶段时,浏览器会清空任务队列,也就是说任务队列中尚未被执行的任务不再执行,正在执行的任务有可能被杀死
所以,这个POST请求有可能直接被清空,或者中止执行。

可以用同步请求吗

可以。但有两个缺点:

  1. 会阻塞主线程。
  2. 新版浏览器不支持。

可以阻塞跳转吗

让用户完成发送后,再跳转至新的页面。

document.getElementById('link').addEventListener('click', async (e) => {
  e.preventDefault();

  // Wait for response to come back...
  await fetch("/log", {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    }, 
    body: JSON.stringify({
      some: 'data'
    }),
  });

  // ...and THEN navigate away.
   window.location = e.target.href;
});

同样有两个缺点:

  1. 增加用户跳转延时。
  2. 无法监听非点击式跳转。

推荐做法

1. 监听'visibilitychange'事件,而不是点击事件。

这样,能够监听其它离开页面的行为。

2. 用以下两种请求方式

2.1 用 fetch()提供的 keepalive 选项,确保请求能够发送。

<a href="/some-other-page" id="link">Go to Page</a>

<script>
  document.getElementById('link').addEventListener('click', (e) => {
    fetch("/log", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      }, 
      body: JSON.stringify({
        some: "data"
      }), 
      keepalive: true
    });
  });
</script>

2.2 最佳做法navigator.sendBeacon()

这个接口不抢占资源,用最低的优先级,但确保请求会被发送。不过,这个API不能自定义header, 为了发送 “application/json”格式的数据,需要利用Blob技巧。

document.addEventListener('visibilitychange', function logData() {
  if (document.visibilityState === 'hidden') {
   const blob = new Blob(
    [JSON.stringify({ some: "data" })], 
    { type: 'application/json; charset=UTF-8' }
    );
    navigator.sendBeacon('/log', blob));
  }
});
上一篇下一篇

猜你喜欢

热点阅读