什么是路由
维基百科对路由的说法是
路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动。 路由引导分组转送,经过一些中间的节点后,到它们最后的目的地
概念看起来很模糊,但是用大白话说:就是用户通过某种输入(选择)等途径分发请求,这就是路由。
前端路由的由来
传统的后端路由的特点是通过url发送请求给后端服务器,然后通过服务器返回的代码渲染页面。但是这样的形式缺点也很明显,就是服务器压力较大,并且整体渲染效果受网络等因素影响,体验较差。
AJAX的出现给前端带来了生机,前端也引申出SPA的单页面应用概念。而通过前端路由来向用户展示出不同的页面,这样页面就会更加流畅,大大提高用户的体验。
前端路由基本概念
前端路由实际上就是在一个html下通过修改某样东西来给用户看到不同的内容界面。
那么这个东西是什么呢?目前的解决方案是localstorage跟url,通过他们来和渲染的界面形成对应关系。
SPA
SPA 是 single page web application 的简称,译为单页Web应用。 简单来说,它就是不会进行多页面跳转,而是在单一的页面内,通过获取用户的输入,得知其希望进入的内容,然后在单页面内分别展示对应的页面内容。
传统页面与SPA的区别
传统页面实际上就是我建n个HTML文件,来达到不同页面效果的跳转,这样对用户来说,不同的界面就意味着进入了不同的HTML文件。
SPA界面就是我只建一个HTML文件,把所有不同的界面都塞到这个文件中,当用户选择时,我就把对应的内容展示给他看。
上面的代码,将视图1跟视图2都写入div中,放入container里,然后通过前端路由将它渲染出来
案例模拟
hash模式
比如www.baidu.com/#1 这个#1就是hash值
hash模式就是url上面使用#来告诉浏览器,用户需要看哪个页面。因为url的#后面不会发送给服务器,而且hash事件的改变可以引发hashchange事件,所以拿来做前端路由非常方便。
<a href="#1">to id1</a>
<a href="#2">to id2</a>
<div class="container">
<div id="id1">id1</div>
<div id="id2">id2</div>
<div id="id404">您的页面不存在</div>
</div>
上面的代码我定义了不同的a标签,当点击不同的a标签时,页面会改变hash值。
//父元素
let parent = document.querySelector(`.container`);
//路由表,存了用户想进入的各种界面
const routesTable = {
"#1": document.querySelector(`#id1`),
"#2": document.querySelector(`#id2`)
};
//render函数用来获取hash值,设置默认路由,渲染界面
function render() {
let hash = window.location.hash; //拿到#1 #2内容
if (!hash) {
hash = "#1"; //默认路由
}
for (let child of parent.children) {
child.style.display = "none";
}
if (!routesTable[hash]) {
//保底路由
let div = document.querySelector("#id404");
div.style.display = "block";
} else {
routesTable[hash].style.display = "block";
}
}
render();
window.addEventListener("hashchange", () => {
//hashchange事件
render();
});
上面的js代码就是hash模式下的代码,使用url的#来对应render函数,渲染页面。并且设置了保底路由跟404路由。
hash模式的缺点是由于#后面的内容不能发送给服务器,所以SEO并不友好。
history
history其实就是摈弃了#这样的方式,你可以直接使用路径/来对应界面,我们需要在这种模式下解决两个问题:
- 需要监控到url栏
/修改 - 让页面不要自动刷新
解决方法:
在history api中,新增了history.pushState和history.replaceState两个api,它可以对url的地址进行修改并且不会刷新页面,这样也可以模拟hash模式的url跳转,但是它并不会引起hashchange事件。所以我们要修改一下上面的代码。
<a href="/1">to id1</a>/*这里改了*/
<a href="/2">to id2</a>
<div class="container">
<div id="id1">id1</div>
<div id="id2">id2</div>
<div id="id404">您的页面不存在</div>
</div>
<script src="index.js"></script>
//父元素
let parent = document.querySelector(`.container`);
//路由表,存了用户想进入的各种界面
const routesTable = {
//路由表换成‘/’的的形式
"/1": document.querySelector(`#id1`),
"/2": document.querySelector(`#id2`)
};
//render函数用来获取hash值,设置默认路由,渲染界面
function render() {
let hash = window.location.pathname; //拿到/1 /2
if (hash === "/") {
hash = "/1"; //默认路由
}
for (let child of parent.children) {
child.style.display = "none";
}
if (!routesTable[hash]) {
//保底路由
let div = document.querySelector("#id404");
div.style.display = "block";
} else {
routesTable[hash].style.display = "block";
}
}
render();
let allA = document.querySelectorAll("a");
for (let a of allA) {
//绑定a的所有点击事件
a.addEventListener("click", (e) => {
e.preventDefault();//不跳转页面 不刷新
//此时将页面的url修改掉
window.history.pushState(null, "", a.getAttribute("href"));
render();
});
history由于是根据/路径符来操作的,所以就需要后端将内容页面都渲染到单个的页面。
这种方法弥补了hash模式SEO不足的情况,缺点是ie8以下不支持(谁在乎啊?)
memory
这种方式实际上用的就是localstorage替代url栏来存放数据,我们只需要将数据保存进localstorage,需要使用时拿出来用就可以了
...//由于跟history的代码一致只修改了三行代码,就将相同的代码用...表示
//路由表,存了用户想进入的各种界面
const routesTable = {
...
};
//render函数用来获取hash值,设置默认路由,渲染界面
function render() {
let hash = window.localStorage.getItem("xx");
if (!hash) {
hash = "/1"; //默认路由
}
...
}
...
for (let a of allA) {
...
window.localStorage.setItem("xx", a.getAttribute("href"));
...
});
}
memory模式适合只有一个路径、或者非浏览器(比如app)的情况。
缺点是由于使用的是存储在本机上的数据,如果想要分享页面,那么会给新用户展示默认页面。