前端路由
路由的概念来源于服务端,在服务端中路由描述的是 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
事件
- 可以通过拦截
pushState/replaceState
的调用和<a>
标签的点击事件来检测URL是否发生变化
实现方式(1):hash
<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
<!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标签点击事件并拦截。