前端路由
什么是前端路由
在web前端单页应用SPA(Single Page Application)中,路由描述的是URL和UI之间的映射关系,这种映射是单向的,即URL变化引起UI更新(无需刷新页面)
如何实现前端路由
实现前端路由,需要解决两个核心:
- 如何改变URL却不引起页面刷新
- 如何检测URL变化了
目前主要有两种实现方式,分别是hash和history。
hash实现
hash是URL中hash(#)及后面的那部分,常用作锚点在页面内进行导航,改变URL中的hash部分不会引起页面刷新。
通过hashchange事件监听URL的变化,改变URL的方式只有以下这几种:
- 通过浏览器的前进后退改变URL
- 通过标签改变URL
- 通过window.location改变URL
- 通过手动修改改变URL
history实现
history提供了pushState和replaceState两个方法,这两个方法改变URL的path部分,不会引起页面刷新
history提供类似hashchange事件的popstate事件,但popstate事件有些不同:
- 通过浏览器前进后退改变URL时会触发popstate事件
- 通过pushState/replaceState或标签改变URL不会触发popState事件
- 但是我们可以拦截pushState/replaceState的调用和标签的点击事件来检测URL变化
- 通过js调用history的back,go,forward方法触发该事件
所有history监听URL变化可以实现,但是没有hashchange那么方便,但是用户体验上相对会更好,因为在URL上不会出现#。
利用原生js简单实现hash和history
原生js实现hash
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<!-- 定义路由 -->
<li><a href="#/home">home</a></li>
<li><a href="#/about">about</a></li>
</ul>
<!-- 渲染路由对应的UI -->
<div id="routerView"></div>
</body>
<script>
let routerView = document.querySelector('#routerView');
// 通过hashchange监听URL的变化,渲染对应的UI
window.addEventListener('hashchange', ()=>{
let hash = location.hash;
routerView.innerHTML = hash
})
// 监听页面加载完成后,将当前路由对应的UI进行渲染
window.addEventListener("DOMContentLoaded",()=>{
if(!location.hash){ //如果不存在hash值,那么重定向到/
location.hash = '/'
}else{ //如果存在hash值,那就渲染对应的UI
let hash = location.hash;
routerView.innerHTML = hash
}
})
</script>
</html>
原生js实现history
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<!-- 定义路由 -->
<li><a href='/home'>home</a></li>
<li><a href='/about'>about</a></li>
<!-- 渲染路由对应的UI -->
<div id="routeView"></div>
</ul>
</body>
<script>
let routerView = routeView;
window.addEventListener('DOMContentLoaded', onLoad);
window.addEventListener('popstate', ()=>{
routerView.innerHTML = location.pathname;
})
function onLoad(){
routerView.innerHTML = location.pathname;
var linkList = document.querySelectorAll('a[href]');
linkList.forEach(el => el.addEventListener('click', function(e) {
e.preventDefault();
history.pushState(null, '', el.getAttribute('href'));
routerView.innerHTML = location.pathname;
}))
}
</script>
</html>