![ad5c3b71ea584a96c592339e694c25ad.png](https://img-blog.csdnimg.cn/img_convert/ad5c3b71ea584a96c592339e694c25ad.png)
说起路由,那么就先来聊聊上古时期的网页是怎么渲染的吧。服务端工程师在后台把页面都构建好,根据浏览器的请求地址,去匹配相应的页面,输出的都是已经生成好的HTML,点击跳转都会重新刷新出一个新的页面。
为了提升用户体验,单页应用(SPA)应运而生,良好的体验,丝般柔滑的页面切换,让人心旷神怡。既然页面由客户端渲染了,路由也随之而来。现在用React或Vue做个页面,谁不加个Router包呢。那么用的是爽的,大家知道其中的原理吗?
不妨在此先说一说前端控制路由的两种形式:
1、hash
大家常常看到的hash就是浏览器上的a标签作为锚点去使用,点击的时候浏览器地址改变了#/**这种新式,并且页面是不会刷新的。上面说的React和Vue所用的路由包,也有hash的形式去切换路由。浏览器原生的提供了一个监听事件“hashchange”,a标签改变url,浏览器的前进后退,window.location改变地址栏的地址 都会触发“hashchange”监听事件,通过这个事件去获取localtion.hash,继而去匹配相应的组件。代码大致如下
<!DOCTYPE html>
<html>
<head>
<title>hash路由</title>
</head>
<body>
<div>
<ul>
<li><a href="#/page1">page1</a></li>
<li><a href="#/page2">page2</a></li>
</ul>
<div id="route-view"></div>
</div>
<script type="text/javascript">
// 下面为hash的路由实现方式
// 第一次加载的时候,不会执行hashchange监听事件,默认执行一次
window.addEventListener('DOMContentLoaded', Load)
window.addEventListener('hashchange', HashChange)
var routeView = null
function Load() {
routeView = document.getElementById('route-view')
HashChange()
}
function HashChange() {
switch(location.hash) {
case '#/page1':
routeView.innerHTML = 'page1'
return
case '#/page2':
routeView.innerHTML = 'page2'
return
default:
routeView.innerHTML = 'page1'
return
}
}
</script>
</body>
</html>
2、history
通过history去控制路由的话,那就比较麻烦了,因为history提供的“popstate“监听事件监听不到pushState、replaceState、a标签这三种形式的改变,浏览器的前进后退是可以监听到的。有什么好的解决方案呢?遍历页面上的所有a标签,然后阻止它的默认事件,加上点击事件回调函数,在函数内,获取a标签的href属性值,再通过pushState去改变浏览器上的pathname,最后手动执行一次popstate监听的回调函数,匹配相应的路由。代码如下
(⚠️注意,以下代码不能直接打开浏览器访问,因为地址是本地地址,会有问题,需要启动服务打开)
<!DOCTYPE html>
<html>
<head>
<title>hash路由</title>
</head>
<body>
<div>
<ul>
<li><a href="/page1">page1</a></li>
<li><a href="/page2">page2</a></li>
</ul>
<div id="route-view"></div>
</div>
<script type="text/javascript">
// 下面为history的路由实现方式
window.addEventListener('DOMContentLoaded', Load)
window.addEventListener('popstate', PopChange)
var routeView = null
function Load() {
routeView = document.getElementById('route-view')
PopChange()
var aList = document.querySelectorAll('a[href]')
aList.forEach(event => event.addEventListener(function(e) {
e.preventDefault() //阻止a标签的默认事件
var href = e.getAttribute('href')
history.pushState(null, '', href)
PopChange()
}))
}
function PopChange() {
switch(location.pathname) {
case '/page1':
routeView.innerHTML = 'page1'
return
case '/page2':
routeView.innerHTML = 'page2'
return
default:
routeView.innerHTML = 'page1'
return
}
}
</script>
</body>
</html>
那么说了那么多,大家也应该对Link和a标签的区别有所了解了。就是默认情况下用a标签,默认事件是不会被阻止的,所以点击a标签,页面会刷新。而Link内部阻止的a标签的默认事件,转而去获取它的href值去pushState改变pathname,不会引起页面的刷新。以上