路由
在传统的多页面应用(MPA) 中,服务端路由是非常常见的。当用户在浏览器中输入 URL 或点击链接时,浏览器会向服务器发送请求,服务器端的路由系统会解析请求的 URL,并确定应该返回哪个HTML页面。每个URL对应服务器上的一个独立的资源或页面。
在单页面应用(SPA) (vue就是单页面应用)中,情况有所不同。SPA是一种Web应用程序架构,其核心思想是在加载应用程序时只加载一个HTML页面,并通过JavaScript动态地更新该页面,而不需要每次交互都向服务器请求新的HTML页面。
单页应用只有一个html页面,页面切换url不变,想保持原来服务端路由的特点,来维持url和组件的映射关系,这就是前端路由要解决的问题。
实现前端路由需要解决的问题
单页应用在一般页面变化时url并不会直接改变,但是我们想模拟传统多页应用中路由显示特点,希望页面切换url对应也变化,于是我们需要考虑两个核心问题
- 如何修改url,还不引起页面的刷新(跳转)
- 如何知道url变化了
我们可以通过js实现路由的hash模式和history模式来解决
hash
浏览器中url后面拼接 #xxx 会被认为是hash值,而hash值的变更,是不会引起浏览器页面的刷新。我们就能实现页面切换同步url变化,但是页面不跳转。
特点
在网址后加'/'回车后网页刷新
在网址后加其它内容回车后网页刷新
在网址后面加'#'后接任意内容回车不刷新
我们编写一个页面来实现hash模式下的前端路由
代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
ul {
display: flex;
}
li {
display: inline-block;
}
</style>
</head>
<body>
<ul>
<li><a href="#/home">首页</a></li>
<li><a href="#/about">关于</a></li>
</ul>
<div id="routerView">
内容显示区
</div>
<script>
const routes = [
{
path: '#/home',
component: '首页页面内容'
},
{
path: '#/about',
component: '关于页面内容'
}
]
const routerView = document.getElementById('routerView')
window.addEventListener('hashchange', onHashChange);
function onHashChange(event) {
// 处理URL变化的逻辑
//console.log(window.location.hash);//直接打印location也是一样的效果
routes.forEach((item, index) => {
if (item.path === location.hash) {
routerView.innerHTML =
item.component
}
});
}
//解决只能点击才会触发onHashChange事件
window.addEventListener('DOMContentLoaded',onHashChange)
</script>
</body>
</html>
代码解释
-
一般修改url的主要方式:
- a标签
- 浏览器的前进后退
- window.location的前进后退
-
这里我们通过a标签中跳转的路径开头设置为'#',这样点击a标签后,页面url后面添加上a标签中设置的路径,因为是变更了url的哈希部分,所以页面并没有刷新
-
在script标签中定义一个对象数组,每个对象有跳转路径属性path,以及跳转之后的要展示的内容component
-
设置一个监听事件,监听hashchange(更改当前页面url中的哈希部分),当url改变就会执行函数onHashChange
-
onHashChange会读取当前的window对象的location.hash属性也就是url中的哈希部分,再遍历对象数组,如果该对象路径path和当前url的哈希部分匹配就将其component拿到页面中展示
效果
history
因为前端路由的hash模式增加'#'号,有点不好看,当然也有去掉'#'更简洁的history模式
原理:
-
解决页面刷新问题 : history模式下,更改url之后页面会被刷新,但是我们想更改url又不想刷新页面,js中提供了一个pushState方法,可以修改url且不会引起页面刷新;
-
解决组件和url的映射问题 : 那我们也要完成url与组件的映射关系可以通过location.pathname 属性获取URL中的路径部分,之后通过匹配对应的url展示相应的页面内容,通过js提供的popState事件,仅当浏览器前进后退时该事件生效,来实现页面通过浏览器前进后退时url与组件的正确映射
代码:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
ul {
display: flex;
}
li {
display: inline-block;
}
</style>
</head>
<body>
<ul>
<li><a href="/home">首页</a></li>
<li><a href="/about">关于</a></li>
</ul>
<div id="routerView">
放一个代码片段
</div>
<script>
const routerView = document.getElementById("routerView");
const routes = [
{
path: '/home',
component: '首页页面内容'
},
{
path: '/about',
component: '关于页面内容'
}
]
const links = document.querySelectorAll('li a')
// console.log(links);
links.forEach(a => {
a.addEventListener('click', (e) => {
// console.log(e);
//阻止了a标签的默认跳转行为
e.preventDefault();
//添加一种可以修改url又不造成页面刷新
history.pushState(null, '', a.getAttribute('href'));
//映射对应的DOM
onPopState()
})
})
function onPopState() {
// console.log(location.pathname);
routes.forEach((item) => {
if (item.path == location.pathname) {
routerView.innerHTML = item.component
}
});
}
function onLoad() {
onPopState()
const links = document.querySelectorAll('li a')
links.forEach(a => {
//为每个a标签添加点击事件
a.addEventListener('click', (e) => {
e.preventDefault() // 阻止了a标签的默认跳转行为
// 添加一个可以修改url又不造成页面刷新
history.pushState(null, '', a.getAttribute('href'))
// 映射对应的dom
onPopState()
})
})
}
window.addEventListener('DOMContentLoaded', onLoad)
//监听页面因为浏览器前进后退导致的url变化事件
window.addEventListener('popstate', onPopState)
</script>
</body>
</html>
代码解释:
- 因为点击a标签会更改url,而history模式下,更改url会导致页面刷新,这不是我们想要的效果,所以我们使用a标签的自带函数preventDefault(),阻止了a标签的默认跳转行为
- 那么此时a标签就不能修改url,当然js中有我们需要的修改url但是不引起页面刷新的方法pushState,我们给a标签绑定点击事件,点击事件执行自定义函数onPopState,在onPopState中使用pushState方法来更改url
- 自定义函数onPopState,函数通过读取当前页面的url的路径部分location.pathname,再遍历路径对象,如果路径参数path匹配就将该对象的component展示到页面上
- 在浏览器ui界面进行前进后退,改变url的话并没有触发点击事件,所以我们需要为这个情景补充一个监听事件window.addEventListener('popstate', onPopState),popstate事件,仅在浏览器ui界面进行前进后退,改变url触发
效果:
总结
1. 背景
在传统多页面应用(MPA)中,服务端路由常见,而单页面应用(SPA)通过前端路由实现页面切换和导航,从而提升用户体验。本文讨论了前端路由的简易实现,重点介绍了 hash 模式和 history 模式。
2. hash 模式
- 特点: 在 URL 后面加
#
,不会引起页面刷新。 - 解决问题: 实现页面切换同步 URL 变化,但不进行页面跳转。
- 实现原理: 利用
hashchange
事件监听 URL 变化,通过对象数组映射 URL 和组件关系,实现页面内容的动态切换。
3. history 模式
- 特点: 去除
#
,更简洁。 - 解决问题: 使用
pushState
方法修改 URL 且不引起页面刷新,解决页面刷新问题;通过popState
事件,和a标签的点击事件实现 URL 与组件的映射关系。 - 实现原理: 利用
popstate
事件监听浏览器前进后退,通过location.pathname
获取 URL 路径部分,映射到相应组件。