关于前端路由
首先什么是路由?
路由就是通过互联的网络把信息从源地址传输到目的地址的活动。这些活动的传送端和接收端也许是比较复杂不止一个的,而路由则引导两端传输如何进行,经过一些中间的节点后,到它们最后的目的地,这个过程我们称作 “分发请求”。路由有很多种形式:单播,多播,任播,广播及地域性广播。
而我们平常使用的路由器则是具有路由功能的硬件设备。
前端路由及一些简单概念
- 前端路由:根据不同的url或者其他的标识内容,请求各自对应的内容并展现出来。
- 路由表:存储路由引导规则的哈希表。
- hash:包含块标识符(#)的DOMString,开头有一个“#”。
- 默认路由:当路径中没有特定的路由标识时,默认导向的路由。
- 404路由:又称:保底路由,当路径中的路由在路由表中没有匹配的地址时跳转的路由。
- 嵌套路由:2层及以上的路由。
前端路由的三种模式
-
hash模式
-
概念:以地址(url)中的hash作为路由识别标识的前端路由模式。
-
优点:
- 兼容性好,达到了ie8,且绝大多数框架都支持,所以绝大多数情况下都能够使用hash模式。
- 利用a标签跳转锚点,除了发送Ajax及资源加载的请求外,不会发送别的请求。
- 不需要再服务端进行设置。
-
缺点:
-
URL丑
-
SEO不友好:
因为服务器接收不到hash。比如,在输入 www.baidu.com/#123 ,在控制台Network下,我们可以发现,我们发送请求的URL 还是 www.baidu.com 所以百度的服务器是接收不到hash的。这就导致N个不同的路由 最后都是导向了 www.baidu.com 。搜索引擎就认为这N个路由都是www.baidu.com这个页面,所以最后只会展示www.baidu.com的页面,而其他路由则搜索不出来。
不过谷歌搜索引擎针对hash模式新出了一个功能——hashbang,能够识别hash,不过要在#后面加个!如:#!123。
-
-
-
history模式(browser模式)
- 概念:由于实现过程中使用了HTML5中的history的api,所以称作:history模式。
- 优点:
- 正常的URL,比较容易处理。
- 绝大数框架也支持history模式。
- 后端能够准确追踪到路由。
- 缺点:
- 兼容性不如hash,只兼容到了ie10
- 需要后端的支持,所有的前端路由都要跳转到同一个页面,一般来说是index.html,然后根据JS中的逻辑以及url中的pathname进行页面渲染。 如果不这样做直接输入某路由的地址将会出现404报错。
-
memory模式
概念:在内存中模拟一个堆栈来管理历史记录。
这个模式已经很少使用,适用的情况很少,一般只会应用于app。
而且由于不同终端上的记录存储不同,会导致内容无法实现分享,该模式又称作:“单机路由”。
实现一个简单hash的路由
<!-- html -->
<body>
<!-- 使用的是锚点,不会发生跳转 -->
<a href="#1" class='link'>one</a>
<a href="#2" class='link'>two</a>
<a href="#3" class='link'>three</a>
<a href="#4" class='link'>four</a>
<div id="app"></div>
<script src="./index.js"></script>
</body>
// js
// 需要展示的各个dom
const div1 = document.createElement('div')
div1.innerHTML = 'one'
const div2 = document.createElement('div')
div2.innerHTML = 'two'
const div3 = document.createElement('div')
div3.innerHTML = 'three'
const div4 = document.createElement('div')
div4.innerHTML = 'four'
// 挂载容器
const container = document.querySelector('#app')
// 路由表
const routeMap = {
'1': div1,
'2': div2,
'3': div3,
'4': div4,
}
route()
// 监听hash的变化
window.addEventListener('hashchange', () => {
route()
})
function route() {
let number = window.location.hash.substr(1) // 获取hash值并处理
number = number || '1' // 默认路由
container.innerHTML = ''
if (routeMap[number]) {
container.appendChild(routeMap[number])
} else {
// 404路由
const notFound = document.createElement('div')
notFound.innerHTML = 'notound'
container.appendChild(notFound)
}
}
实现一个简单的history路由
<!-- html -->
<body>
<!-- 这里使用的是路径,注意后续对a标签默认事件的处理 -->
<a href="/1" class="link">one</a>
<a href="/2" class="link">two</a>
<a href="/3" class="link">three</a>
<a href="/4" class="link">four</a>
<div id="div404" style="display:none">该页面不存在</div>
<div id="app"></div>
<script src="./index.js"></script>
</body>
// js
// 需要展示的各个Dom
const div1 = document.createElement("div");
div1.innerHTML = "one";
const div2 = document.createElement("div");
div2.innerHTML = "two";
const div3 = document.createElement("div");
div3.innerHTML = "three";
const div4 = document.createElement("div");
div4.innerHTML = "four";
const app = document.querySelector("#app");
// 路由表
const routeMap = {
"/1": div1,
"/2": div2,
"/3": div3,
"/4": div4
};
route(app);
function route(container) {
// history的路由的key是路径名
let number = window.location.pathname;
// 默认路由
if (number === "/") {
number = "/1";
}
container.innerHTML = "";
let div = routeMap[number];
// 404路由
if (!div) {
div = document.querySelector("#div404");
}
div.style.display = "block";
container.appendChild(div);
}
const allA = document.querySelectorAll("a.link");
for (let a of allA) {
// 监听a标签的click事件,每次点击修改url,同时渲染对应的内容
a.addEventListener("click", e => {
// 取消a标签的跳转功能
e.preventDefault();
// 通过history的api来操作url
window.history.pushState(null, null, a.href);
route(app);
});
}