路由概念

  • 服务器端:url 和文件资源的映射关系。

  • 前端:url 和组件的映射关系。

实现前端路由需要解决的问题

  1. 如何修改 url 还不引起页面的刷新

  2. 如何知道 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、图片 等资源 按需加载 页面组件以构成新的页面。当然,当新组件或页面需要新资源或者已有资源缓存过期时,会向服务器发送请求来更新资源。

  • 前端路由接管后续的路由切换,这些切换通常只涉及在浏览器本地利用缓存加载或渲染新的组件,而不需要服务器参与。

文章参考:稀土掘金社区 - 谈谈前端路由的实现原理【hash&history】