如何精确统计页面停留时长

简介:

1.背景

页面停留时间(Time on Page)简称 Tp,是网站分析中很常见的一个指标,用于反映用户在某些页面上停留时间的长短,传统的Tp统计方法会存在一定的统计盲区,比如无法监控单页应用,没有考虑用户切换Tab、最小化窗口等操作场景。 基于上述背景,重新调研和实现了精确统计页面停留时长的方案,需要 兼容单页应用和多页应用,并且不耦合或入侵业务代码。

2.分析

我们可以把一个页面生命周期抽象为三个动作: 「进入」、「活跃状态切换」、「离开」

963c1dc053607b13c6ee11d4f549cdcb5a7a288c

如下图,计算页面停留时长既如何监控这三个动作,然后在对应触发的事件中记录时间戳,比如要统计活跃停留时长就把 active 区间相加即可,要统计总时长既 tn -t0 。

664c74c39dbd5762515ae5e110d7af456ae2baa5

2.1 如何监听页面的进入和离开?

对于常规页面的 首次加载、页面关闭、刷新 等操作都可以通过 window.onload 和 window.onbeforeunload 事件来监听页面进入和离开,浏览器前进后退可以通过 pageshow 和 pagehide 处理。

● load / beforeunload
● pageshow / pagehide

对于单页应用内部的跳转可以转化为两个问题:

● 监听路由变化
● 判断变化的URL是否为不同页面 。

2.1.1 监听路由变化

目前主流的单页应用大部分都是基于 browserHistory (history api) 或者 hashHistory 来做路由处理,我们可以通过监听路由变化来判断页面是否有可能切换。注意是有可能切换,因为URL发生变化不代表页面一定切换,具体的路由配置是由业务决定的(既URL和页面的匹配规则)。

browserHistory

路由的变化本质都会调用 History.pushState() 或 History.replaceState() ,能监听到这两个事件就能知道。通过 popstate 事件能解决一半问题,因为 popstate 只会在浏览器前进后退的时候触发,当调用 history.pushState() or history.replaceState() 的时候并不会触发。

The popstate event is fired when the active history entry changes. If the history entry being activated was created by a call to history.pushState() or was affected by a call to history.replaceState(), the popstate event’s state property contains a copy of the history entry’s state object.

Note that just calling history.pushState() or history.replaceState() won’t trigger apopstateevent. The popstate event will be triggered by doing a browser action such as a click on the back or forward button (or calling。history.back() or history.forward() in JavaScript).

这里需要通过猴子补丁(Monkeypatch)解决,运行时重写 history.pushState 和 history.replaceState 方法:

 
let _wr = function ( type ) {
let orig = window.history[type]
return function () {
let rv = orig.apply(this, arguments)
let e = new Event(type.toLowerCase())
e.arguments = arguments
window.dispatchEvent(e)
return rv
}
}
window.history.pushState = _wr('pushState')
window.history.replaceState = _wr('replaceState')
window.addEventListener('pushstate', function (event) {})
window.addEventListener('replacestate', function (event) {})

hashHistory

hashHistory 的实现是基于 hash 的变化,hash 的变化可以通过 hashchange 来监听

2.1.2 判断URL是否为不同页面

方案1: 客户端定义

通过业务方在初始化的时候配置页面规则,然后JS通过URL匹配不同的规则来区分不同的页面,这种方案在客户端数据上报的时候就已经明确了不同的页面,伪代码:

 
new Tracer ({
rules: [
{ path: '/index' },
{ path: '/detail/:id' },
{ path: '/user', query: {tab: 'profile'} }
]
)

方案2: 数据分析平台定义

假设我们最终上报后有一个数据分析平台来展现,我们可以在类似数据平台来配置页面规则,这样在客户端实现的代码逻辑就不需要区分页面,而是每次URL发生变化就将数据上报,最终通过数据平台配置的页面URL规则来求和、过滤数据等。

当数据展现平台不支持配置URL规则来区分页面的时候,可以采用方案1;当有数据平台支持的时候采用方案2更合理;

2.1.3 对于页面进入和离开相关事件整理

44a184de1e80933e3d6f6583916d9821f002b2d6

2.2 如何监听页面活跃状态切换?

可以通过 Page Visibility API 以及在 window 上声明 onblur/onfocus 事件来处理。

2.2.1 Page Visibility API

一个网页的可见状态可以通过 Page Visibility API 获取,比如当用户 切换浏览器Tab、最小化窗口、电脑睡眠 的时候,系统API会派发一个当前页面可见状态变化的 visibilitychange 事件,然后在事件绑定函数中通过 document.hidden 或者 document.visibilityState 读取当前状态。

 
document . addEventListener ( 'visibilitychange' , function ( event ) {
console.log(document.hidden, document.visibilityState)
})

2.2.2 onblur/onfocus

可以通过 Page Visibility API 以及在 window 上声明 onblur/onfocus 事件来处理。 对于PC端来说,除了监听上述相关事件外,还可以考虑监听鼠标行为,比如当一定时间内鼠标没有操作则认为用户处于非活跃状态。

2.3 什么时机上报数据?

2.3.1 页面离开时上报

对于页面刷新或者关闭窗口触发的操作可能会造成数据丢失

2.3.2 下次打开页面时上报

会丢失历史访问记录中的最后一个页面数据

目前采用的方案2,对于单页内部跳转是即时上报,对于单页/多页应用触发 window.onbeforeunload 事件的时候会把当前页面数据暂存在 localStorage 中,当用户下次进入页面的时候会把暂存数据上报。有个细节问题,如果用户下次打开页面是在第二天,对于统计当天的活跃时长会有一定的误差,所以在数据上报的同时会把该条数据的页面进入时间/离开时间带上。

3.设计

3.1 UML类关系图

Tracer

核心类,用来实例化一个监控,对原生事件和自定义事件的封装,监听 enter activechange exit 事件来操作当前 Page 实例。

P.S. 取名来自暴雪旗下游戏守望先锋英雄猎空(Tracer),直译为:追踪者。

Page
页面的抽象类,用来实例化一个页面,封装了 enter exit active inactive 等操作,内部通过 state 属性来维护当前页面状态。

32d154586548adf3972e0d6c8d78ba7dcfa0c8f7

3.2 事件派发关系图 2b9733f17243b77b1090e272c0da5ed61498baa8
4.兼容性

Desktop

28f44131b5cd9881de6496633f6ca8c60bdecb90

Mobile

028b3292288e09b15b185753f964899e57852787

5.思考

对于页面停留时长的定义可能在不同场景会有差异,比如内部业务系统或者OA系统,产品可能更关心用户在页面的活跃时长;而对于资讯类型的产品,页面可见时长会更有价值。单一的数据对业务分析是有限的,所以在具体的代码实过程中我们会把停留时长分三个指标,这样能更好的帮助产品/运营分析。

● active 页面活跃时长
● visible 页面可见时长 //仅支持Desktop

● duration 页面总停留时长

6.参考

● https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onhashchange
● https://developer.mozilla.org/en-US/docs/Web/Events/popstate
● https://developer.mozilla.org/en-US/docs/Web/API/PageVisibilityAPI
● https://stackoverflow.com/questions/4570093/how-to-get-notified-about-changes-of-the-history-via-history-pushstate


原文发布时间为:2018-09-4

本文来自云栖社区合作伙伴“前端大学”,了解相关信息可以关注“前端大学”。

相关文章
|
5月前
|
JavaScript
实时显示当前时间,每秒更新
实时显示当前时间,每秒更新
|
13天前
|
存储 测试技术 定位技术
需要统计出轨迹点出入某个区域的信息,包括:驶入时间、驶出时间
Lindorm Ganos 通过内置的 `ST_TrajectoryProfile` 算子高效统计轨迹的出入信息,利用时空索引技术减少扫描量和内存使用,降低计算成本。它通过空间索引+过滤下推减少扫描量,聚合加速提升效率,并在聚合算子内部完成进出点判断和轨迹信息提取。然而,该算子受限于时间阈值设定,可能在轨迹点不均匀采集时产生误差。测试环境下,查询耗时在20秒内,具体表现取决于过滤后的数据量和空间范围复杂度。
|
2月前
|
存储 定位技术
Booking.com如何在毫秒内搜索数百万个地点
Booking.com如何在毫秒内搜索数百万个地点
33 1
淘宝批量复制宝贝提示“当前类目大于48小时发货的发货时间不能大于15天,请调整”怎么解决?
要复制这个宝贝上传到淘宝店铺,只需要重新复制一次,然后在大淘营淘宝宝贝复制专家下载配置的第二步,选择一个小于或等于15天的发货时间(见下图),这样就可以复制宝贝上传到淘宝店铺了。
|
存储 缓存 Dart
如何处理直播实时在线人数显示并且最小化性能和资源消耗?
直播技术成为一种极为流行的交流方式。而直播平台的核心指标之一就是实时在线人数,准确地显示该指标对于用户和运营商来说都具有重要意义。然而,直播实时在线人数的显示也面临着性能和资源消耗的挑战。本文将介绍如何利用Flutter和Dart开发技术栈来优化直播实时在线人数的显示,以达到最小化性能和资源消耗的目标。 作者:狗头大军之江苏分军 链接:https://juejin.cn/spost/7255473856234913852 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
如何处理直播实时在线人数显示并且最小化性能和资源消耗?
|
前端开发 JavaScript NoSQL
[前端]每间隔一段时间查询接口获取到百分比,在页面上按照百分比展示进度条,百分比进度后下载文件
[前端]每间隔一段时间查询接口获取到百分比,在页面上按照百分比展示进度条,百分比进度后下载文件
|
前端开发 JavaScript
Threejs实现波动热点效果,波动标记,波动标签显示
Threejs实现波动热点效果,波动标记,波动标签显示
447 0
Threejs实现波动热点效果,波动标记,波动标签显示