浏览器地址变换监听,popstate/pushstate/replacestate事件
发布于 作者:苏南大叔 来源:程序如此灵动~

需求是这样的:页面监控浏览器里地址栏里的url
变化情况。那么问题来了,页面的url
变化,(虚假),页面加载的源码就会发生变化,那么相关的程序就会发生重载,无法持续进行地址的监控。所以,这似乎存在着一个悖论。那么,真实的情况是怎么样的呢?

苏南大叔的“程序如此灵动”博客,记录苏南大叔和计算机代码的故事。本文测试环境:chrome@130.0.6723.92
。
需求悖论
这个事情从需求到实现,都存在着悖论。比如:
虚假的悖论
事实上,地址栏显示的网址发生变化,页面【不一定】会重新到服务器请求源码的。为了持续监控地址变化,需要保证页面不被刷新。举例来说:
- 普通的页面,变化
#
后面的hash
的时候,页面也是不刷新的。 react
和vue
们,它们就是只变化地址栏,而不重新请求服务器的。
因此,对于页面仅变化hash
,或者react
和vue
的情况来说,页面并不刷新,持续监控地址栏变化是可行的。
真实的悖论
因为监控的手段就是监听相关的地址变化事件(仅变化#hash
,也属于其中)。相关事件一共有四个。分别是:
pushstate
,产生新的地址。popstate
,在老地址之间变换。例如:历史前进或者历史后退动作。replacestate
,替换当前的地址。hashchange
,变换#hash
时触发,但同时会触发pushstate
或popstate
。
出乎意料的是,只有popstate
事件是可以被监控到的。另外的pushstate
和replacestate
事件,无法被监控到。参考文章:
也就是说,正常情况下,只有点击左上角的前进后退按钮,或者代码执行history.back()
等事件的时候,才能被监控到popstate
事件。
改写函数打补丁
为了解决上述问题,这里利用函数改写的方法,在.pushstate()
和.replacestate()
函数中,hook
了对应的事件。从而在外部能够对这两个事件做出对应的反应,这算打个系统补丁。
有关技术实现的细节,可以参考文章:
- https://newsn.net/say/js-hook.html
- https://newsn.net/say/func-args.html
- https://newsn.net/say/js-apply.html
- https://newsn.net/say/js-custom-event.html
pushstate
replacestate
完整测试代码
值得一提的是:这段代码必须放在nginx
之类的服务器下,才能正常使用。双击是不行的,会得到错误提示信息:
完整的测试代码如下:

这段代码运行后,其实还可以得出结论:
- 就是这些
pushstate
/popstate
/replacestate
/hashchange
,都不会影响来源页的变化【待议】,也就是说当前的页面确实还没有发生变化,只是变标没变本。 hashchange
可能伴随popstate
或者pushstate
。
事件监听
打完补丁后,核心监控代码就这样:
结束语
更多苏南大叔的经验文章,请参考:


