背景
为了解用户在我们H5页面的行为习惯,我们需要统计和上报用户在H5具体某个页面的停留时长。
当我们的H5页面是一个vue单页面应用,我们需要具体统计到每个路由的停留时长。
本文记录的是,我们自己实现的一套页面停留时长统计上报的方案,最后具体落地实施也是按这个方案走的。
当然市面上也有很多现成的方案,如果没有特殊的需求,现成的方案应该是可以满足大多数的场景的。
方案设计
问题一:停留时长的统计
通过调研,单页面应用统计页面时长,有以下这几种方案:
(1)页面在打开状态下,每隔一段时间给服务端发一个请求,由服务端计算页面时长;
(2)在页面打开和关闭的时候,分别给服务端上报一个请求,由服务端计算页面时长;
(3)前端监听页面的打开和关闭,计算出页面停留时长,直接上报服务端。
结合我们的应用场景,第3种方案是比较合适的,接下来主要需要解决的问题是如何监听页面的打开和关闭事件,对于单页面应用,就是下面这两个问题。
1.如何监听全部的路由跳转事件?
路由跳转事件相对来说是比较明确的,vue-router给我们提供了很多路由守卫可以使用。我们用beforeEach做拦截,除了首次进入,后面每次beforeEach都是前一个页面的结束。同时在beforeEach中,我们可以记下即将进入的路由和被退出的路由名称,供上报使用。
2.是否能够监听全部的关闭事件?
首先,判断回退事件是否可以监听到,我们考虑这三个事件:pagehide、onunload、onbeforeunload。
pagehide
当页面隐藏的时候触发,跳转到新页面和关闭浏览器或者webview退到后台,都会触发这个事件。
pagehide兼容性比较好,几乎可以不考虑兼容性的问题。
onunload
该事件在关闭窗口资源和内容的时候触发。页面资源的清除工作会在 unload 事件之后进行。
值得注意的是,MDN上对onunload有这样一段描述:
onunload 特性(乃至 unload 事件本身) 并非使用 sendBeacon()的正确途径,要调用 sendBeacon() 接口,应当使用 visibilitychange 和 pagehide 事件。 参见 Beacon API is broken。
浏览器兼容情况,这里需要试验的浏览器太多,我们直接找到网上的一个结论:
- IE浏览器 页面跳转、刷新页面能执行,但关闭浏览器不能执行;
- firefox 页面跳转能执行,但刷新页面、关闭浏览器不能执行;
- Safari 刷新页面、页面跳转,关闭浏览器都会执行;
- Opera、Chrome任何情况都不执行。
IE, Firefox, 和 Safari 支持 onunload 事件, 但是 Chrome 或者 Opera 不支持该事件。
onbeforeunload
当窗口即将被卸载(关闭)时,会触发该事件.此时页面文档依然可见,且该事件的默认动作可以被取消.
可用于弹出对话框,提示用户是继续浏览页面还是离开当前页面。
为了更好的验证这些事件的可用性,测试一下兼容性,我们对这些事件做一个小实验。验证事件是否触发的过程,需要用localStorage存储来实现,因为alert无效,console.log来不及看(其实也是无效的,即使Preserve log了也是不会输出的)
代码语言:javascript复制window.addEventListener('pagehide', () => {
window.localStorage.setItem('pagehide', dateFormat(new Date(), 'MM-dd hh:mm:ss'));
});
window.addEventListener('unload', () => {
window.localStorage.setItem('unload', dateFormat(new Date(), 'MM-dd hh:mm:ss'));
});
通过在我们页面常用的几个浏览器中测试,可以得到这样的结果。
可以看到除了onunload事件在游戏内的slugsdk浏览器没有触发,其他环境下都是有触发的,这个其实跟兼容性关系也不大,应该是sdk或者游戏对webview做了优化,关闭webview的时候其实是去隐藏webview,下次打开还是复用这个window,从sessionStorage不被清除也可以看出来确实是这样的。
iOS微信 | iOS QQ | iOS Slugsdk | 安卓微信 | 安卓QQ | 安卓Slugsdk | |
---|---|---|---|---|---|---|
pagehide | 有 | 有 | 有 | 有 | 有 | 有 |
onunload | 有 | 有 | 无 | 有 | 有 | 无 |
我们比较pagehide和onunload在不同浏览器和环境下的兼容性,发现优先使用pagehide事件,onunload事件作为兜底,来记录关闭事件,是比较合适的方案。
总结一下,单页面的路由跳转用beforeEach做拦截记录,对于关闭窗口的情况,用上面说到的两个事件pagehide|onunload,来做记录。
问题二:统计数据的上报
时长统计完成后,就是对于数据的上报了,我们可以考虑两种上报方案:
1.每次页面结束就上报
2.把时长数据存本地,批量上报
对每次页面结束就上报,可能存在关闭窗口导致页面上报请求未发送的问题,解决这个问题有一些解决方案,比如Beacon API,但我们这边暂时不会用到,就不展开讨论了。
实际上,结合我们业务的应用场景,其实批量上报的方案更符合我们的需求,实现起来也更简单。
我们可以把时长数据存储在localStorage,启动一个定时器,定时取出数据做上报,上报完成再清除本地已上报的数据。
到这里页面停留时长统计上报的方案就大致确定了,如果有发现可以改进的地方,欢迎评论。