一、思考
离开页面,发送请求会面临两个问题:
- ajax请求是异步的,当页面跳转的时候,ajax请求会被取消,导致数据发送不成功。
- 移动端 unload 监听,有可能不触发。 要解决这两个问题,才可以落实页面跳转埋点。
二、解决问题
- 查阅资料,了解到使用 navigator.sendBeacon()方法可用于通过 [HTTP POST]将少量数据[异步]传输到 Web 服务器。
MDN 描述:
这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向web服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而,对于开发者来说保证在文档卸载期间发送数据一直是一个困难。因为用户代理通常会忽略在
unload
事件处理器中产生的异步XMLHttpRequest
。
使用
sendBeacon()
方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能
来到这里,这个方法不就是为这样的埋点量身定做的吗?再看下兼容性,发现事情没有那么简单!
Ios系统需要去到11.3版本才支持,结合到用户手机的使用情况,这个方法只能暂时搁置,后续我们可以做优雅降级。
再往回看,MDN 其实已经告诉我们,有其他解决办法:
因为公司服务器负荷问题,第三个方法直接排除!那就尝试下其他两个办法。
- 尝试在 unload 使用 ajax 同步请求。核心代码:
xhr.open('post', 'url', false); // true :异步请求, false:同步请求
结果,果然没我想得那么简单, 报错了!
看了下报错信息,原来谷歌浏览器已经不允许在 beforeunload
, unload
, pagehide
,visibilitychange
发送同步请求了,详细可以看官网描述!
- 尝试使用 创建
<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抓包,配置好过滤器,只查看相应请求信息:
从Fiddler上看,数据已经发送成功,返回 200
,询问后端之后,这堆数据都已成功上报!证明这个方法是行得通的!
- 优雅降级
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)
}
unload
方法在移动端不一定能够触发,我们可以改用监听visibilitychange
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
sendData(data)
}
}, false);