路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。
在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。
实现前端路由
核心:
- 不引起页面刷新
- 检测URL变化
方法:
- hash:URL中(#)及(#)后面的部分。通过
hashchange
事件监听URL变化,改变URL的方式:- 通过浏览器前进后退改变URL
- 通过
<a>
标签改变URL - 通过
window.locatioin
改变URL
- history:提供
pushState
和replaceState
两个方法:- 提供
popstate
事件,但popstate
事件有些不同:- 通过浏览器前进后退改变URL时会触发
popstate
事件 - 通过
pushState/replaceState
或<a>
标签改变URL时不会触发popstate
事件
- 通过浏览器前进后退改变URL时会触发
- 可以通过拦截
pushState/replaceState
的调用和<a>
标签的点击事件来检测URL是否发生变化
- 提供
实现方式(1):hash
代码语言:javascript复制<body>
<!-- route -->
<ul>
<li>
<a href="#/home">home</a>
</li>
<li>
<a href="#/about">about</a>
</li>
</ul>
<!-- view -->
<div id="routerView"></div>
<script>
window.addEventListener('DOMContentLoaded',onLoad)
//监听路由变化
window.addEventListener('hashchange',onHashChange)
let routerView = null;
function onLoad() {
routerView = document.querySelector('#routerView');
onHashChange();
}
function onHashChange() {
switch(location.hash) {
case '#/home':
routerView.innerHTML = 'HOME';
return;
case '#/about':
routerView.innerHTML = 'About';
return;
default:
routerView.innerHTML = '';
return;
}
}
</script>
</body>
实现方式2:history
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<ul>
<li>
<a href="/home">home</a>
</li>
<li>
<a href="/about">about</a>
</li>
<div id="routeView"></div>
</ul>
<script>
window.addEventListener('DOMContentLoaded',onLoad)
//监听路由变化
window.addEventListener('popstate',onPopState)
//routeView
let routeView = null;
function onLoad () {
routeView = document.querySelector('#routeView');
onPopState()
//拦截a标签点击事件,点击时使用pushState修改URL并手动更新routeView
let linkList = document.querySelectorAll('a[href]')
linkList.forEach(el => el.addEventListener('click',function(e){
e.preventDefault()
history.pushState(null,'',el.getAttribute('href'))
onPopState()
}))
}
function onPopState() {
switch (location.pathname) {
case '/home':
routeView.innerHTML = 'HOME'
return
case '/about':
routeView.innerHTML = 'About'
return
default:
routeView.innerHTML = ''
return
}
}
</script>
</body>
</html>
我们看到,使用history
的方式,需要多判断a标签点击事件并拦截。