探索下vue-router的hash和history模式的原理
以下分析是针对vue-router 3.x版本
模式
vue-router有hash模式和history模式,这个一般都知道,但是除了这两个之外,还有一个模式叫abstract模式,适用于在node服务端,没有浏览器的history api。
不过abstract模式模式用的比较少,主要是hash和history模式。
hash和history模式
那么它们的原理是什么?
我们通过阅读它们的源码来分析下。
在vue-router项目对应目录下可以找到文件
hash模式
hash模式是url会有个 # 号,后面就是hash的内容,这个就叫hash模式。
hash模式会先判断当前浏览器是否支持history的pushState api,
如果支持,就直接监听popstate事件,否则有个兜底,hashchange事件。
针对push方法和replace方法,他也是有兜底策略
如果支持history的pushState,则直接调用history.pushState方法,否则会直接赋值hash,触发hashchange事件
同理replace方法也是,兜底会调用location.replace方法。
history模式
history模式的url就没有 # 号,这个模式需要后端支持。
它是直接监听popstate事件
然后push方法和replace方法是使用history.pushState和history.replaceState直接跳转。
history api新增
HTML5的history api新增了pushState和replaceState方法,以及hashchange事件和popstate事件。下面来讲讲它们的用法,这样就可以更好的理解上面vue-router的写法。
pushState方法
pushState方法是会新增一条历史记录,并且更新url的地址,但是不会刷新页面。你可以点击返回返回到刚才的页面。
history.pushState(state, title, url)
有三个参数
- state 状态对象,必传,会存储在历史记录中,可以通过history.state或者popstate事件事件对象的state属性获取
- title 字符串,标题,必传,目前浏览器都会忽略这个title,可以传''
- url 可选,可以是相对地址和绝对地址,但是不能跨域,否则会报错。
history.pushState({name: '答案cp3'}, "title", "http://qq.com"); // VM2030:1 Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'http://qq.com/' cannot be created in a document with origin 'https://www.baidu.com' and URL 'https://www.baidu.com/'.
history.pushState({name: '答案cp3'}, "title", "/abc");
console.log(location.href) // 'https://www.baidu.com/abc'
replaceState方法
replaceState方法是不会新增一条历史记录,而是替换当前url地址,也不会刷新页面。所以你点击返回不会返回到上一级,而是上上一级(如果存在)
参数等同pushState方法的参数,这里不再赘述。
hashchange事件
它是监听url的hash变化,只要hash有变化就会触发hashchange事件,比如赋值location.hash,location.replace等。
popstate事件
它是监听当前页面历史记录变化事件,哪些情况可以触发,比如:
- 浏览器的返回/前进按钮
- history.back()/history.forward()/history/go() 等方法
- location.hash或者location.replace
要注意:调用history.pushState() 和 history.replaceState()不会触发该事件。
如果触发的时候,当前的url是调用 history.pushState() 修改或者history.replaceState() 创建的,就会在事件对象上有一个state属性,如果不是则是null。
window.addEventListener('popstate',(e) => {
console.log(e.state)
})
history.pushState({name: '答案cp3'}, "title", "?test")
history.pushState(null, null, '?test1')
history.back()
可以看到,当往url插入2条历史记录,然后调用返回键,此时url是调用history.pushState(),所以打印的了{name: '答案cp3'}
总结
vue-router的hash和history模式主要是使用到了history的pushState方法和replaceState方法。事件监听使用到了popstate事件。
如果不支持上面两个方法,hash模式会有兜底策略,兜底可以使用location.hash和location.replace方法代替pushState方法和replaceState方法,另外监听hashchange事件。
为什么vue-router会使用pushState方法和replaceState方法?
主要是因为它们调用不会刷新页面,这样可以保留页面的数据,体验比刷新页面会好一点。
如果还有其他答案欢迎评论交流。
本文正在参加「金石计划 . 瓜分6万现金大奖」