写在前面
不知道大家有没有在 router 上犯过傻,最近被小伙伴难倒了一下,做个记录,是什么呢,她按往常一样通过 localhost:8080 打开了自己本机的项目,但是这次 “手贱”,自己不仅输入了ip地址,还输入了主页的路径 localhost:8080/overview,打开后发现并没有进到主页,然后一直在纠结为啥没有进入,而这个时候被强行拉进来的我,可能也是脑袋瓜不够清醒,一不小心就被带偏了,也是一直顺着她的方式,检查来检查去确实路径没啥问题,耽误了好几分钟才恍然发现,原来是路由哈希没写上的搞的鬼,终于真相大白,所以借此做下记录,也趁机温习一下 vue-router 原理吧。
前端路由原理
hash 模式
hash 模式是 vue router 的默认模式,如果平时不怎么关注 vue-router 的小伙伴可能都不会注意到,原来vue-router 默认的模式就是 hash
一般的 url 组成成分
url: juejin.cn:8080/creator?sta…
location.protocal => https
location.hostname => juejin.cn
location.host => juejin.cn:8080
location.port => 8080
location.pathname => /creator
location.search => ?status=all
location.hash => #/home
hash 特点分析
虽然 hash 模式只是 hash 变化,真正的路径并没有变化,但是 hash 依然能够触发网页跳转,也支持浏览器的前进和后退。如果平时观察仔细我们还会发现 hash 的页面跳转方式其实很特殊,hash 变化时,页面并不会刷新,只是内容进行了替换,而内容替换这种行为完全由前端控制,与 Server 端无关,因此这种模式使用这种模式的应用也被称为 SPA 应用
代码演示
实现 hash 主要利用 window 的 onhashchange 这个钩子函数,当监听到 url 中的 hash 变化时,调用 onhashchange 这个函数,完成相应的动作,下面通过一段简单的 hash 切换代码演示 hash 切换的监控, 这里设置了一个按钮,用于跳转到 Yimwu_Home 这个页面,通过这个跳转来验证 onhashchange 是否如我们所想的那样,能够成功监听到 hash 变化
<body>
<h3>Old Hash: <span id="oldhash"></span></h3>
<h3>Current Hash: <span id="newhash"></span></h3>
<button id="btn">Goto Yimwu_Home</button>
<script>
const btn = document.getElementById('btn')
// 给按钮绑定事件
btn.addEventListener('click', () => {
location.href = '/#/yimwu-home'
})
// hash 监听
window.onhashchange = (hashevent) => {
const oldhash = document.getElementById('oldhash')
const newhash = document.getElementById('newhash')
// 将新、旧路径赋值到页面对应位置
oldhash.innerText = hashevent.oldURL
newhash.innerText = hashevent.newURL
console.log(hashevent)
}
</script>
</body>
复制代码
如上图所示,当通过按钮跳转到 yinwuhome 时,发生了 hash 变化,触发 onhashchange,因此old Hash 与 Currend Hash 被更新,当点击浏览器前进和后退时, onhashchange 同样也监听到了 hash 变化,再次对 old Hash 与 Currnent Hash 进行更新,通过上面简单的实验,应该大致就可以理解 hash 模式的设计方式了吧。
history 模式
history 模式特点分析
与 hash 相似的是 history 模式也不刷新页面,唯一和 hash 不同的是 history 所采用的路径规范不同,hash 采用的是路径末端的 hash 变化来切换路由,而 history 模式是通过模拟路径变化来切换路由。
代码演示
实现 history 主要使用的是 history.pushState 和 window 上的 onpopstate 钩子函数,下面依然通过一个简单的路由切换按钮来演示路由切换过程
<body>
<h3>Current path: <span id="newpath"></span></h3>
<button id="btn">Goto Yimwu_Home</button>
<script>
const btn = document.getElementById('btn')
const newpath = document.getElementById('newpath')
btn.addEventListener('click', () => {
const state = {name: 'yimwu-home'}
// 不会触发 onpopstate 钩子
history.pushState(state, '', 'yimwu-home')
// ============= 路由切换逻辑 =============== //
newpath.innerText = location.pathname
})
// 监听 path 变化,浏览器前进、后退触发
window.onpopstate = (event) => {
console.log(event)
newpath.innerText = location.pathname
}
</script>
</body>
复制代码
如上图所示,当通过按钮跳转到 yinwuhome 时,可以观察到 Path 的变化,但是此时并没有触发 onpopstate。onpopstate 只会在浏览器前进、后退时触发,因此,在 history 模式下,我们路由切换的逻辑需要写在 history.pushState 触发后,也就是说当执行 pushState 时,就是需要切换路由的时刻,那么此时前端就需要进行相应的页面渲染替换动作的执行。