1. 前端router是什么
在传统的html编程中,如果想要实现页面能够展示不同页面那么我们就得写很多的html页面来根据url进行展示,但是在vue项目中是一个单页应用的状态,在vue项目中只有一个index.html,所有的页面或者叫组件都是在这个html中展示,而vue中的router就可以实现根据不同url展示对应的组件。
router: 通过url路径来匹配对应的代码块的这么一套机制
router特点:
- 修改 url 后,页面要更新
- 浏览器不刷新
- 如何监听 url 变更
2. hash路由实现
const router = createRouter({
history: createWebHistory(),
routes: routes
})
当我们在vue项目中配置路由的时候,里面有一个属性叫history,这个属性有两个值其中的createWebHashHistory() 就是我们常说的hash模式,在这个模式中我们的url路径中会多加一个#除此之外与createWebHistory() 并无区别。
在简单回忆了一下router的hash是什么之后,接下来我们来根据router特点和hash模式来自己实现一下router中的hash模式,首先我们需要了解一个小知识点:
#/home 在浏览器的 url 中出现 # 号,#后面的内容会被看作是一个 hash 值,hash 值的变化不会引起页面的重新加载
在我们了解了这一个特点之后,我们就已经完成了router中的修改url之后浏览器不刷新这一特点,接下来我们只需要监听url变更,并且在修改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>
</head>
<body>
<ul>
<li><a href="#/home">首页</a></li>
<li><a href="#/about">关于</a></li>
</ul>
<div id="app"></div>
<script>
// 路由
const routes = [
{
path: '/home',
component: () => {
return '<h2>首页页面</h2>'
}
},
{
path: '/about',
component: () => {
return '<h3>about页面</h3>'
}
},
]
</script>
</body>
</html>
如果我们想要实现router剩下的两个特点,那么就需要对页面中url的变化进行监听,当页面中的url变化的时候,在页面执行对应的routes[x].component()就可以在页面展示对应的组件。这个功能我们用一个函数routerView(localHash)来实现加载对应路径组件这个功能:
const app = document.getElementById('app')
function routerView(localHash) {
// 去 routes 数组中查找 localHash 值在哪一项中
// 如果找到了,就将这一项中的component执行结果放到 app 容器中
const index = routes.findIndex((item) => {
return '#' + item.path === localHash
})
app.innerHTML = routes[index].component()
}
要想实现上述监听url功能,我们就得用官方为我们提供的'hashchange'事件,这个事件可以监听页面中hash值的变化,我们在url中用了#将后面的值变成了hash值,而不同的页面就会展示不同的url就会让hash值进行改变,这样就能通过这个事件来加载对应的组件。
// 浏览器自带的一个监听hash变化事件
window.addEventListener('hashchange', () => {
// console.log(location.hash);// 获取到url变化
routerView(location.hash)
})
同时我们需要在页面完成加载时,就展示对应的组件,完成这个功能才算实现了hash路由,这个功能我们通过官方提供的'DOMContentLoaded'事件来监听页面加载完成,接下来我们来看完整代码:
<!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">首页</a></li>
<li><a href="#/about">关于</a></li>
</ul>
<div id="app"></div>
<script>
const routes = [
{
path: '/home',
component: () => {
return '<h2>首页页面</h2>'
}
},
{
path: '/about',
component: () => {
return '<h3>about页面</h3>'
}
},
]
const app = document.getElementById('app')
function routerView(localHash) {
// 去 routes 数组中查找 localHash 值在哪一项中
// 如果找到了,就将这一项中的component执行结果放到 app 容器中
const index = routes.findIndex((item) => {
return '#' + item.path === localHash
})
app.innerHTML = routes[index].component()
}
// 监听页面加载完毕
window.addEventListener('DOMContentLoaded', () => {
routerView(location.hash)
})
// 浏览器自带的一个监听hash变化事件
window.addEventListener('hashchange', () => {
// console.log(location.hash);// 获取到url变化
routerView(location.hash)
})
</script>
</body>
</html>
3. history路由实现
通过上文我们知道了history路由的路径中是没有#的,这样的话我们如果变换路径的时候那就必然会引起页面的刷新,我们实现history路由的时候要避免这一点。
首先我们页面中的基础html结构同上,我们同样需要一个routerView()函数来实现对应url组件加载功能:
const app = document.getElementById('app')
function routerView() {
const index = routes.findIndex(item => {
return item.path === location.pathname // 可以读取本地url
})
app.innerHTML = routes[index].component()
}
接下来我们想要实现的效果是点击对应的a标签就展示对应的组件,我们在点击a标签的时候就会进行a标签的默认跳转事件,这个时候我们只需要对要实现router效果的a标签阻止它的默认行为,然后修改url路径后执行routerView()函数即可:
history.pushState() 方法可以修改url,不会带来页面刷新
// 获取页面中所有的a标签
let linkList = document.querySelectorAll('a')
linkList.forEach(link => {
link.addEventListener('click', function(e) {
// 让a标签不跳转,阻止默认行为
e.preventDefault()
// 让 url 发生变化
history.pushState(null, '', this.getAttribute('href'))
routerView()
})
})
在实现了url变更展示对应组件和页面不刷新两点功能之后,还有一个小点得注意一下,当我们点击浏览器左上角前进后退的时候,页面也要展示对应url的组件,仅靠上述代码是实现不了的,这时候我们就需要使用'popstate'事件来实现这个效果,接下来直接给大家展示完整代码:
<!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">首页</a></li>
<li><a href="/about">关于</a></li>
</ul>
<div id="app"></div>
<script>
const routes = [
{
path: '/home',
component: () => {
return '<h2>首页页面</h2>'
}
},
{
path: '/about',
component: () => {
return '<h3>about页面</h3>'
}
},
]
// 监听浏览器前进后退按钮
window.addEventListener('popstate', function () {
routerView()
})
let linkList = document.querySelectorAll('a')
linkList.forEach(link => {
link.addEventListener('click', function(e) {
// 让a标签不跳转
e.preventDefault() // 阻止默认行为
// 让 url 发生变化
history.pushState(null, '', this.getAttribute('href')) // 可以修改url,不会带来页面刷新
routerView()
})
})
const app = document.getElementById('app')
function routerView() {
const index = routes.findIndex(item => {
return item.path === location.pathname // 可以读取本地url
})
app.innerHTML = routes[index].component()
}
</script>
</body>
</html>
总结
hash 路由: #/home 在浏览器的 url 中出现 # 号,#后面的内容会被看作是一个 hash 值,hash 值的变化不会引起页面的重新加载,从而直接监听hashchange事件来判断要展示的对应的组件
history 路由: 首先阻止 a 标签的默认行为,然后使用 history.pushState() 方法来修改 url 路径,它是不会引起页面的重新加载的,在url变更后,读取本地的 pathname 来判断要展示的对应的组件