前端路由的实现原理【hash&history】
路由概念
-
服务器端:url 和文件资源的映射关系。
-
前端:url 和组件的映射关系。
实现前端路由需要解决的问题
-
如何修改 url 还不引起页面的刷新
-
如何知道 url 变化了
Hash 模式路由
-
(浏览器特殊机制)在浏览器 url 后加个哈希值,哈希值的变更不会引起浏览器页面的刷新(不向服务器请求资源)。
-
浏览器环境下自带一个 "hashchange" 事件,它可以自动监听 hash 值的变更。
-
window.location 中存在 url 中的 hash 值。
-
页面首次加载时,不会去加载当前路由,即不会触发 hashchange 事件,这时需要监听 DOMContentLoaded 来执行一次 onHasChange 回调。
<body>
<!-- 模拟SPA hash.html -->
<ul>
<li><a href="#/home">首页</a></li>
<li><a href="#/about">关于</a></li>
<!-- 判断url的变化,有 hashchange 的官方方法 -->
</ul>
<div id="routeView">
<!-- hash 路由变化后,对应组件的出口-->
</div>
<script>
const routes = [
{ path: '#/home', component: '首页页面内容' },
{ path: '#/about', component: '关于页面内容' }
]
const routeView = document.getElementById('routeView')
window.addEventListener('DOMContentLoaded', onHashChange)
window.addEventListener('hashchange', onHashChange)
// 路由改变,动态切换组件
function onHashChange() {
routes.forEach((item, index) => {
if (item.path === location.hash) {
routeView.innerHTML = item.component
}
})
}
</script>
</body>
History 模式路由
路由中不再有 hash 值,那 history 如何实现 url 切换而不引发页面刷新呢?它又如何监听到 url 变化呢?
-
浏览器中有个会话历史栈,它可以维护你的访问路径,有了这个你返回就可以按照栈的顺序进行前进回退。
-
HTML DOM API 提供了 History 对象,其中 pushState 方法可以修改 url 且不引起页面的刷新。
- 浏览器环境下可通过 "popstate" 事件监听 url 变化,仅当浏览器前进后退时生效。
<body>
<ul>
<li><a href="/home">首页</a></li>
<li><a href="/about">关于</a></li>
</ul>
<div id="routeView"> </div>
<script>
const routes = [
{ path: '/home', component: '首页页面内容' },
{ path: '/about', component: '关于页面内容' }
]
const routeView = document.getElementById('routeView')
window.addEventListener('DOMContentLoaded', onLoad)
window.addEventListener('popstate', onPopState)
function onLoad() {
const links = document.querySelectorAll('li a') // 获取所有的li下的a标签
links.forEach(a => {
// 禁用a标签的默认跳转行为
a.addEventListener('click', e => {
e.preventDefault() // 阻止a的跳转行为
history.pushState(null, '',
a.getAttribute('href')) // 核心方法 a.getAttribute('href')获取a标签下的href属性
// 映射对应的dom
onPopState()
})
})
}
function onPopState() {
routes.forEach(item => {
if (item.path === location.pathname) {
routeView.innerHTML = item.component
}
})
}
</script>
</body>
总结
-
使用 Vue/React 所写的单页面应用,当第一次请求 url 时,其对应的资源会按需返回,提高页面加载速度以及应用程序性能。
-
前端路由变化时,一般不会去向服务器重新请求资源,而是利用本地缓存的 js、css、图片 等资源 按需加载 页面组件以构成新的页面。当然,当新组件或页面需要新资源或者已有资源缓存过期时,会向服务器发送请求来更新资源。
-
前端路由接管后续的路由切换,这些切换通常只涉及在浏览器本地利用缓存加载或渲染新的组件,而不需要服务器参与。