路由本质
路由的本质是分发。其作用就是分发请求,把对应的请求分发到对应的位置。
原生 JS 实现路由
// index.html
<a href='#1'>go to 1</a>
<a href='#2'>go to 2</a>
<a href='#3'>go to 3</a>
<a href='#4'>go to 4</a>
<div id='app'></div>
<div id="div1" style="display:none">1</div>
<div id="div2" style="display:none">2</div>
<div id="div3" style="display:none">3</div>
<div id="div4" style="display:none">4</div>
<div id="div404" style="display:none">不存在此页面</div>
<script src="index.js"></script>
// index.js
// 获取用户想去哪里
let number = window.location.hash.substr(1);
// (默认路由)number 若存在即为 number 否则 number 为 1
number = number || 1
// 获取界面
let div = document.querySelector(`div${number}`);
let app = document.querySelector("#app");
// 渲染界面
if(div){
div.style.display = "block";
} else{
// 保底路由
div = document.querySelector('div404');
div.style.display = "block";
}
// 展示界面
app.appendChild(div)
// 当 url 变化时触发 hashchange 事件
window.addEventListener("hashchange", () => {
const number2 = window.location.hash.substr(1);
let div2 = document.querySelector(`div${number2}`);
if(div2){
div2.style.display = "block";
} else {
div2 = document.querySelector('div404');
div2.style.display = "block";
}
const app2 = document.querySelector("#app");
app2.children[0].style.display = "none";
app2.appendChild(div2)
});
// 上面代码优化为 路由表
const div1 = document.createElement("div");
div1.innerHTML = "1";
const div2 = document.createElement("div");
div1.innerHTML = "2";
const div3 = document.createElement("div");
div1.innerHTML = "3";
const div4 = document.createElement("div");
div1.innerHTML = "4";
const routeTable = {
"1":div1,
"2":div2,
"3":div3,
"4":div4,
}
function route(){
let number = window.location.hash.substr(1);
let app = document.querySelector("#app");
number = number || 1;
let div = routeTable[number.toString()];
if(!div){
div = document.querySelector('div404');
}
div.style.display = "block";
if(app.children.length > 0){
app.children[0].style.display = "none";
document.body.appendChild(app.children[0]);
}
app.appendChild(div);
}
route();
window.addEventListener("hashchange", () => {
route();
});
后端路由
服务器将浏览器请求的URL解析之后映射成对应的函数,这个函数根据资源类型的不同进行不同的操作,静态资源就进行文件读取,动态数据就会通过数据库进行增删改查的操作。
本质是URL请求地址与服务器资源之间的对应关系。
前端路由
由于单页面应用SPA的兴起,前端页面 = 组件化,不同的页面就是不同的组件,页面的切换就是组件的切换,页面切换不需再通过 http 请求,直接通过 JS 解析 URL 地址,然后找到对应的组件进行渲染。
与后端路由最大的不同是:不需要再经过服务器,直接在浏览器下通过 JS 解析页面之后就可以拿到相应的页面。
几种模式
| 模式 | 使用场景 | 备注 |
|---|---|---|
| hash | 任何情况能做前端路由 通过 URL 的 hash 存路径 | Vue-router默认路由模式 SEO不友好 |
| history | 只有后端将所有前端路由都渲染同一页面 通过 URL 的 history 存路径 | 基于HTML5的history对象IE8以下不支持 |
| memory | 适合非浏览器,app中使用 不使用 URL | 单机版路由 |
基于 hash 实现
基于URL地址的hash,也就是hash的变化会导致浏览器记录访问历史的变化,但是hash的变化不会触发新的URL请求。
hash值是指URL #后面的内容,通过location.hash属性可读写hash值,通过监听window对象的hashchange事件,就可以感知到hash值变化。
基于 history 实现
history提供了两个函数修改URL,监听URL变化通过监听window对象上的popstate事件来实现。两个函数不会触发popstate事件,需要手动触发页面渲染。
history.pushState() // 新增一个历史记录
history.replaceState() // 直接替换当前的历史记录
还需要服务端设置,将所有URL请求转向前端页面,交给前端进行解析。
路由解析
vue-router和react-router都同时依赖Path-to-RegExp这个第三方库进行路由解析。
- 路由匹配
当获取到请求路径后,如何找到对应的配置路径
- 路由生成 通过配置的请求路径字符串和参数生成对应的请求路径
vue-router 工作流程
url改变 => 触发事件监听 => 改变vue-router中的 current 变量 => 监视 current 变量的监视者 => 获取新的组件 => render渲染