都2022了,还在为跳转页面埋点(发送请求)发愁?

1,120 阅读3分钟

一、思考

离开页面,发送请求会面临两个问题:

  1. ajax请求是异步的,当页面跳转的时候,ajax请求会被取消,导致数据发送不成功。
  2. 移动端 unload 监听,有可能不触发。 要解决这两个问题,才可以落实页面跳转埋点。

二、解决问题

  1. 查阅资料,了解到使用 navigator.sendBeacon()方法可用于通过 [HTTP POST]将少量数据[异步]传输到 Web 服务器。

详细用法可以到官网查看!

MDN 描述:

这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向web服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而,对于开发者来说保证在文档卸载期间发送数据一直是一个困难。因为用户代理通常会忽略在 unload  事件处理器中产生的异步 XMLHttpRequest

使用 sendBeacon()  方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能

来到这里,这个方法不就是为这样的埋点量身定做的吗?再看下兼容性,发现事情没有那么简单!

image.png

Ios系统需要去到11.3版本才支持,结合到用户手机的使用情况,这个方法只能暂时搁置,后续我们可以做优雅降级。

再往回看,MDN 其实已经告诉我们,有其他解决办法:

image.png

因为公司服务器负荷问题,第三个方法直接排除!那就尝试下其他两个办法。

  1. 尝试在 unload 使用 ajax 同步请求。核心代码:
xhr.open('post', 'url', false); // true :异步请求, false:同步请求

结果,果然没我想得那么简单, 报错了!

66321c24d4d5b01d42f855d0dfbdbbe.png

看了下报错信息,原来谷歌浏览器已经不允许在 beforeunload, unload, pagehide,visibilitychange 发送同步请求了,详细可以看官网描述!

  1. 尝试使用 创建 <img/> 标签 设置 src 的形式。核心代码:
// 使用图片方式发送数据
sendDataImage(url, data){
  let img = document.createElement('img');
  const params = encodeURIComponent(JSON.stringify(data))
  img.onload = () => img = null;
  img.src = `${url}?data=${params}`; // 数据形式可以根据自己需求来自行修改
}

代码是写出来了,但是怎么查看有没请求成功呢?页面跳转了,控制面板不一定看得了请求是否发送成功的,你或许可以问后端看看是否发送成功,一次这样可以,但是多次呢?

这里想到抓包神器 Fiddler

ps: 移动端也有抓包工具,请自行搜索

使用Fiddler抓包,配置好过滤器,只查看相应请求信息:

image.png

image.png

从Fiddler上看,数据已经发送成功,返回 200,询问后端之后,这堆数据都已成功上报!证明这个方法是行得通的!

  1. 优雅降级
function sendData(data) {
  if(navigator.sendBeacon) {
     sendDataBeacon(url, data)
  } else {
    sendDataImage(url, data)
  }
}
// 使用 navigator.sendBeacon 发送数据
function sendDataBeacon(url, data) {
  const headers = {
    type: 'application/json'
  }
  const body = new Blob([JSON.stringify(body)], headers)
  navigator.sendBeacon(url, body)
}
  1. unload 方法在移动端不一定能够触发,我们可以改用监听visibilitychange
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
     sendData(data)
  }
}, false);

三、参考文献

  1. MDN
  2. 阮一峰 Page Visibility API 教程