前言
为何要做这个监测用户停留的呢?原由很简单,倘若咱们要分析这个页面对咱们的制品有无价格,那样用户浏览的时长是一个很关键的点,倘若每一个用户平均每日在这个页面停留两个小时以上,那样咱们会觉得这个页面的价值很高;倘若一个页面一月亦没几个用户去浏览,那咱们就会有疑问,这个页面对咱们的制品还有价值吗?咱们的制品后续还要保存它的?这些需要都是能够让咱们思虑是不是要去获取用户停留时长这个功能的。
针对那些应用?多页面应用单页面应用(本文以vue为例子)
怎样去获取用户停留的时长?
在监测功能的时候,咱们首要思虑的便是,咱们的监测代码不可影响咱们此刻的业务代码,和以后的业务代码。
多页面应用
在多页面应用,要获取用户的停留时间还是挺简单的,瞧瞧下面几个Api onload[1] (页面加载完后)onbeforeunload[2] (页面卸载前,亦便是点击叉的时候)onpageshow[3] (页面表示的时候)onpagehide[4] (页面隐匿的时候)经过自己的一点小测试,发掘无论在关闭的时候(亦便是点击叉),还是隐匿的时候(亦便是点击上下箭头)都会触发onpagehide;亦无论是在首次加载,还是刷新的时候,都会触发onpageshow,然则刷新的时候会先触发onpagehide,在触发onpageshow。其他两个API亦是类似,最后,个人选取了用onpageshow和onpagehide这个两个API获取用户停留的时长,你亦能够用其他两个API做。只要在onpageshow初始时间值,在onpagehide的时间求出差值,而后上传到后台就行。
let stopTime
window.onpageshow = ()=>{
stopTime = new Date().getTime()
}
window.onpagehide = ()=>{
stopTime = new Date().getTime() - stopTime
let record = localStorage.getItem(data)
let data = record && JSON.parse(record) || []
localStorage.setItem(data,JSON.stringify([...data,{user:new Date().getTime(),path:window.location.href,stopTime}]))
}
完整代码就那样点,这儿我只是测试,就把模拟的用户id,真实的路径,真实的停留时长存储了在localStorage,在项目中能够传到后台,而后经过分析,再可视化展示出来。
上面效果图(录得GIF就那AV画质,没钱开VIP,将就看吧,有点AV感觉亦好,哈哈),第1次点击的是叉,点击叉那亦能够理解为离开了页面,第二次a链接转,第三次刷新,刷新亦能够理解为一次离开吧,一共存了三条数据,有兴趣自己试试更好理解。
单页面应用
单页面应用可能,会繁杂一点,然则亦繁杂不到哪里去。
单页面应用的路由转,都是基于 H5的History API(browserHistory) 和 Hash(hashHistory)实现的。
browserHistory
单页面的browserHistory路由是基于H5的History API实现的,咱们只要监听popstate就能够晓得,点击前进后退按钮改变的url变化,url出现变化,咱们就能统计用户在该页面待了多长期,代码如下 let timeStr
window.addEventListener(onload,(e)=>{
timeStr =new Date().getTime()
})
window.addEventListener(popstate,()=>{
let t = new Date().getTime() - timeStr
timeStr = new Date().getTime()
console.log(待了时长:+ t)
})
然则,pushState和replaceState(亦便是,点击router-view,$router.push,$router.replace,window.history.pushState,window.history.replaceState不会触发,能够自动试试)不会触发popstate,那咱们就统计不了用户待在该页面的时长的了;可是处理办法还是有的,只需要重写pushState和replaceState,而后监听两个自定义事件就行,看下面代码
// 对原函数做一个拓展
let rewriteHis = function(type){
let origin = window.history[type] // 先将原函数存放起来
return function(){ // 当window.history[type]函数被执行时,这个函数就会被执行
let rs = origin.apply(this, arguments) // 执行原函数
let e = new Event(type.toLocaleLowerCase())// 定义一个自定义事件
e.arguments = arguments // 把默认参数,绑定到自定义事件上,new Event返回的结果,自己上是无arguments的
window.dispatchEvent(e) // 触发自定义事件,把载荷传给自定义事件
return rs
}
}
window.history.pushState = rewriteHis(pushState) // 覆盖原来的pushState办法
window.history.replaceState = rewriteHis(replaceState) // 覆盖原来的replaceState办法
// 监听自定义事件, pushstate事件是在rewriteHis时注册的,不是原生事件
// 当点击router-link 或 window.history.pushState 或 this.$router.push 时都会被该事件监听到
window.addEventListener(pushstate,()=>{})
// 监听自定义事件, replacestate事件是在rewriteHis时注册的,不是原生事件
// 当点击window.history.replaceState 或 this.$router.replace 时都会被该事件监听到
window.addEventListener(replacestate,()=>{})
rewriteHis函数,这个函数重点是对原函数做了一个拓展,上面代码的注释应该说了很清楚了。
browserHistory路由变化监听完整代码
let timeStr
let rewriteHis = function(type){
letorigin =window.history[type]
return function(){
let rs = origin.apply(this, arguments)
let e = new Event(type.toLocaleLowerCase())
e.arguments = arguments
window.dispatchEvent(e)
return rs
}
}
window.history.pushState = rewriteHis(pushState)
window.history.replaceState = rewriteHis(replaceState)
window.addEventListener(onload,(e)=>{
timeStr = new Date().getTime()
})
window.addEventListener(popstate,()=>{
let t = new Date().getTime() - timeStr
timeStr =new Date().getTime()
console.log(待了时长popstate:+ t)
})
window.addEventListener(pushstate,()=>{
let t = new Date().getTime() - timeStr
timeStr = new Date().getTime()
console.log(待了时长pushstate:+ t)
})
window.addEventListener(replacestate,()=>{
let t = new Date().getTime() - timeStr
timeStr = new Date().getTime()
console.log(待了时长replacestate:+ t)
})
这儿的演示就不做任何存储了,爱好的话,自己做一下
hashHistory
hashHistory就简单得不行了,直接监听hashchange就行
window.addEventListener(hashchange,()=>{
let t = new Date().getTime() - timeStr
timeStr = new Date().getTime()
console.log(待了时长:+ t)
})
到这儿为止,单页面应用,多页面应用怎么去获取用户待在该页面的时长就说完了。亦不是很理解,挺简单的。
后续
你以为这就完事了吧,还有一个奇怪的问题。 const router = new VueRouter({
mode:hash,
routes:[...]
})
我在测试hash路由切换的时候,看会不会触发window.addEventListener(hashchange,()=>{}),奇怪的事情出现了,它无触发,却触发了自定义window.addEventListener(replacestate,()=>{}和window.addEventListener(pushstate,()=>{})等History API,到底是为何?我明明把它设置为hash路由了,为何还会触发History API?带着这个疑问,我忍不住的去看了vue-router的源码,最后,解开了自己的疑问,看下面:
在vue-router的hash路由实现文件
有这么一段代码,在 supportsPushState为false时,才会走else规律,else的规律才会触发window.addEventListener(hashchange,()=>{}),那它为何不走?supportsPushState又是什么?pushState,replaceState又是怎么实现的?为何它会触发自定义事件?
supportsPushState
来瞧瞧supportsPushState是什么
上面是supportsPushState的规律,看到这规律,是不是瞬间就明白了为何不走else规律的hash语句了。当满足这些要求才会走else语句(ua.indexOf(Android 2.) !== \-1 || ua.indexOf(Android 4.0) !== \-1) && ua.indexOf(Mobile Safari) !== \-1 && ua.indexOf(Chrome) === \-1 && ua.indexOf(Windows Phone) === \-1,否则其他都是走基于History API实现的Hash-router。
pushState , replaceState
再来瞧瞧这两个api的实现
原来它们都是调用了 History API实现,这就解开了为何它会触发自定义事件的原由了。
看源码指引
倘若你不相信,我比比的话,能够自动瞧瞧源码。
|