美文网首页
离开页面前触发消息推送

离开页面前触发消息推送

作者: alue | 来源:发表于2022-06-25 10:56 被阅读0次

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

    直观做法

        <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));
      }
    });
    

    相关文章

      网友评论

          本文标题:离开页面前触发消息推送

          本文链接:https://www.haomeiwen.com/subject/uurivrtx.html