埋点SDK之页面浏览全埋点实现原理

788 阅读3分钟

背景:埋点SDK需要实现页面浏览全埋点(即由SDK自动采集,无需SDK使用者在代码中手动写入),监听页面浏览,换句话说,也就是监听页面路径的变化。前端主流框架大多支持单页面应用,现在多页面应用应该处于少数,并且多页面应用路由变化监听较为简单,监听popstate即可,所以全埋点实现的难点主要在于单页面应用的场景。

关于单页面应用

单页面应用,顾名思义,就是整个应用只有一个主页面,其余的”页面“,实际上是一个个”组件“,单页面应用中的”页面跳转“,实际上是组件的切换,在这个过程中,只会更新局部资源,页面不会整个刷新。

单页面应用的实现

单页面应用的实现主要有两种方式,hash模式和history模式。

hash模式

hash模式使用URL的hash来模拟一个完整的URL。由于hash本来的作用是锚点,因此,在URL改变时,页面不会重新加载。hash属性位于location对象中,在当前页面中,可以通过下面代码来实现改变当前URL的hash值。

window.location.hash'edit'

执行上述hash赋值后,页面的URL发生改变。赋值前:http://localhost:3000,赋值后:http://localhost:3000/#edit。

在URL中多了以#结尾的hash值,但是赋值前后虽然页面的hash值改变导致页面完整的URL发证了改变,但是页面是不会刷新的。

此外,除了可以通过window.location.hash来改变当前页面的hash值外,还可以通过html的a标签,即锚点,来实现:

<a href="#edit">edit</a>

history模式

history模式是基于HTML5规范,利用history.pushState、history.replaceState API来完成URL跳转而无须重新加载页面。

history.pushState()
history.pushState(stateObj, title, url)
  • stateObj: 状态对象,存储JSON字符串,可以用在popstate事件中
  • title:标题,现在大多浏览器忽略这个参数,直接用null代替
  • url:任意有效地URL,必须与当前页面处在同一个域,用于更新浏览器的地址栏
history.replaceState()
history.replaceState(stateObj, title, url)

history.replaceState与history.pushState的使用非常相似,区别在于pushState会压入浏览器的会话历史栈中,使得history.length加1,而replaceState是替换当前这条会话历史,因此不会增加history.length。

以上介绍了单页面应用的实现原理,那么我们如何监听以上实现带来的URL变化呢?

监听URL中的hash变化

通过hash改变了URL,会触发hashChange事件,只要监听hashChange时间,就能捕获到通过hash改变URL的行为。

window.onhashchange = function(event){
    console.log(event)
}
// 或者
window.addEventListener('hashChange', function(event){
    console.log(event)
})

在监听事件中做我们的埋点事件触发即可。

监听通过history来改变URL的事件

通常history改变URL的几种方法,比如history.back()、history.forward()、history.go(),都会触发popstate事件,但是history.pushState和history.replaceState不会触发popstate事件,举个栗子:

window.addEventListenser('popstate', function(event) {
    console.log(event)
})
window.history.pushState({first: 'first', 'page 2' , '/first'})

上述例子中不会有任何输出,因为并没有监听到popstate事件的发生。

到底如何监听pushState和replaceState,谜底揭晓,我们可以通过改写原有方法,传入我们需要处理的回调函数调用:

image.png

image.png

至此,我们就可以完美实现埋点SDK页面浏览全埋点的需求了。(呱唧呱唧...)