首先我们先明确pv,vv,uv的概念
PV:用户在网站或应用程序上浏览页面的次数。每次用户访问或刷新页面,都会增加页面浏览量的计数。
VV:访客访问的次数,用以记录所有访客一天内访问网站的次数。用户打开网页到关闭网页算作一次。以档次会话session为依据。
UV:独立访客数,指一天内访问某站点的人数,以用户唯一标识为依据。
如何统计pv
进入页面之后,记录当前页面信息和时间,也许你觉得持久化数据没必要,听我娓娓道来。
function afterEnterPage(router) {
// 持久化当前页面信息和时间
const currentPagePath = {
time: Date.now(),
router,
}
window.sessionStorage.setItem(
'_t_currentPagePath',
JSON.stringify(currentPagePath),
)
}
离开页面之前,计算停留时长和当前页面信息
function beforeLeavePage() {
try {
// 获取缓存信息
const currentPagePath = JSON.parse(
window.sessionStorage.getItem('_t_currentPagePath') || '',
)
// 计算停留时长
const stayDuration = Date.now() - currentPagePath.time
return {
stayDuration,
router: currentPagePath.router,
}
} catch (e) {
console.log('session开启')
}
}
使用router.afterEach替代组件内部钩子函数
因为vue-router全局钩子中不存在,进入页面和离开页面的钩子函数,我们可以借用router.after来实现方法调用。
router.afterEach(to => {
// 因为新开tab页from会变成根路径,from信息不可靠
// 信息全部从sessionStorage中获取,获取不到的情况必然是用户第一次开启会话
const data = beforeLeavePage()
console.log('上报页面信息data', data)
// 先执行上个页面上报,再持久化当前页面数据,以供下次使用
afterEnterPage(to)
})
实现思路梳理
- 进入新页面,从sessionStroage中获取上一个页面的信息,完成上报,统计页面PV。
- 统计VV,自然要加上会话id,依然存储在sessionStroage。
- 统计UV,添加上用户的唯一标识,后端就可以分析了。
为什么要持久化上一个页面的信息
我们在afterEach中统计上一个页面信息,如果当前页面是新开页面,from会变成项目根路径。造成统计错误。借用sessionStroage持久化上一个页面信息。
什么情况下sessionStroage会在不同tab页面共享
// 共享sessionStroage的情况
window.open('/about')
<a href="/about" class="" target="_blank" rel="opener">打开新窗口About</a>
// 不共享session的情况
<a href="/about" class="" target="_blank">打开新窗口About</a>
sessionStroage共享问题是chrome 89版本的变动后带来的,可以参考:前端 - Chrome更新89版本后,sessionStorage丢失a标签跳转丢失sessionStorage - 个人文章 - SegmentFault 思否
最后贴上完整代码
packages/tracker · weihua/wh - 码云 - 开源中国 (gitee.com) 此项目基于pnpm构建,packages/tracker是基于vue、阿里云日志服务的sls开发的前端埋点系统。上文中不明白的可以留言或者看完整代码。
至于为什么要自建前端埋点系统,因为目前的三方要么贵,要么不好用。
百度统计和阿里云ARMS,SPA应用都pv、uv都需要手动在router中统计。索性直接在埋点系统中做了。 至于sentry是前端的监控系统,对于业务分析并不是很友好,至少我不知道如何使用sentry做业务分析,有知道的小伙伴可以评论区留言。
import type { Tracker } from '../main'
function afterEnterPage(router: any) {
// 持久化当前页面信息和时间
const currentPagePath = {
time: Date.now(),
router,
}
window.sessionStorage.setItem(
'_t_currentPagePath',
JSON.stringify(currentPagePath),
)
}
function beforeLeavePage() {
try {
// 获取缓存信息
const currentPagePath = JSON.parse(
window.sessionStorage.getItem('_t_currentPagePath') || '',
)
// 计算停留时长
const stayDuration = Date.now() - currentPagePath.time
return {
stayDuration,
router: currentPagePath.router,
}
} catch (e) {
console.log('session开启')
}
}
/**
* chrome浏览器2021年3月份修改了 target="_black" 规则,会造成页面 sessionStorage丢失
* 新开页面需要添加属性 rel="opener" 或者 window.open()。
*/
function pageAnalytics(this: Tracker) {
const { router } = this
router.afterEach(to => {
// 因为新开tab页from会变成根路径,from信息不可靠
// 信息全部从sessionStorage中获取,获取不到的情况必然是用户第一次开启会话
const data = beforeLeavePage()
if (data) {
const params = {
kind: 'pageAnalytics',
path: data.router.path,
fullPath: data.router.fullPath,
nextPath: to.path,
nextFullPath: to.fullPath,
stayDuration: data.stayDuration / 1000,
...this.commonInfo,
}
this.slsTracker.sendImmediate(params)
}
// 先执行上个页面上报,再持久化当前页面数据,以供下次使用
afterEnterPage(to)
})
}
export default pageAnalytics