#yyds干货盘点#讲讲前端路由的原理

时间:2022-10-06 18:57:48

什么是前端路由

前端的路由可以理解成:用来描述 url(web页面的地址) 与 UI(用户界面) 之间的映射关系,就是当 url 发生变化,那么 UI 也会发生变化,那么这种映射关系就叫前端的路由。并且这个路由是一种单向映射的,url==>UI

为什么要有前端路由

随着单页Web应用(说通俗点就是在页面里面如果要查看某条数据的具体内容,但是鼠标点击进去浏览器不会产生一个新的页面)的广大应用,在单页面应用里如何进行数据的变化以及页面的跳转从而使得单页面应用即使只有一个html文件,但是也可以像多页面应用一样包含丰富的内容;此时前端路由就应运而生。

如何实现前端路由

如何实现前端的路由,问题有哪些?

我们都知道在页面上,当页面的url地址改变了,那么这个页面的UI也就会相应的发生改变;我们点击页面上的某个地方,就可能会导致url地址的改变,从而导致UI的改变。

那么第一个问题就来了:当url发生改变的时候,如何检测到url发生变化 ?

解决了第一个问题接下来的问题就是:监测到url发生变化,我们就要渲染相应改变后url的页面,在改变url的同时但是不引起页面刷新,让这个页面至始至终都是一个单页面应用。


方法一:hash

首先我们要知道浏览器也有hash,就是某个页面url地址#后面跟着的东西,例如:

https://blog.51cto.com/blogger/publish?old=1#heading1

并且改变url中的hash值部分,页面是不会刷新的,所以这里就解决了第二个问题,那么如何检测到url发生变化? 我们又要知道浏览器有可以检测到hash值改变的方法:hashchange方法

我们只要检测到hash值的改变,获取到当前改变后的hash值,再根据hash值来切换页面内容就可以了。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<!-- 加了# 浏览器会将其认成一个hash值,从而不刷新页面 -->
<li> <a href="#/home">首页</a></li>
<li><a href="#/about">关于</a></li>
</ul>

<!-- 渲染对应的UI -->
<div ></div>
<script>
let routerView = document.getElementById('routerView')
window.addEventListener('hashchange', onHashChange ) // 浏览器可以检测到hash值改变,监听hash值的改变;如果改变就触发onHashChange函数

// 检测hash值的改变,控制渲染对应的UI
function onHashChange(){
console.log(location.hash); // 本地的hash值
switch(location.hash){
case '#/home':
routerView.innerHTML='Home'
return
case '#/about':
routerView.innerHTML='About'
return
default:
return
}
}
routerView.innerHTML='Home'
</script>
</body>
</html>

方法二:history 模式

history是HTML5提供的一个对象方法,并且它提供三个方法:

  1. pushState
  2. replaceState // 1与2这两个方法都是修改url的,并且这两个方法修改url的时候,是不会引起页面刷新的
  3. popState // 监听url的变化

这三个方法简直就是为前端路由而生呀!

这个方法原理就是:当我们在页面点击某处要切换内容,如果我们不使用hash值就必然会导致页面的刷新,那么我们要做的就是将这个页面跳转遏制住,此时我们就要知道有个阻止默认事件发生的函数:preventDefault();让这个函数阻止页面跳转这个默认行为。然后我们再使用pushState或者replaceState方法与popState方法配合,当我们点击跳转页面时,首先拦截下来跳转这个行为,然后使用pushState或者replaceState方法进行不刷新页面跳转,再监听页面跳转这一行为,然后匹配对应页面的内容,这样前端路由就实现了呀。 代码实现:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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>

<!-- 渲染对应的UI -->
<div ></div>

<script>
let routerView = document.getElementById('routerView') // 控制它显示DOM结构

window.addEventListener('DOMContentLoaded', onLoad) // 一开始加载页面的时候,监听页面加载,调用onLoad函数
window.addEventListener('popstate', onPopState) // 浏览器的前进后退能匹配

function onLoad() {
onPopState() // 匹配路径,展示对应的html

let links = document.querySelectorAll('li a[href]') // 获取a标签的href属性
// 拦截a标签的默认跳转行为
links.forEach(a => {
a.addEventListener('click', (e) => { // 当我们点击a标签时,阻止跳转页面的行为
e.preventDefault() // 阻止a标签的href行为

history.pushState(null, '', a.getAttribute('href')) // 跳转,第三个参数就是要跳转的页面
onPopState() // 再匹配路径,展示对应的html
})
})
}
// 匹配路径,展示对应的html
function onPopState() {
// console.log(location.pathname);
switch (location.pathname) {
case '/home':
routerView.innerHTML = '<h2>home page</h2>'
return
case '/about':
routerView.innerHTML = '<h2>about page</h2>'
return
default:
return
}
}
</script>
</body>
</html>