路由是什么
以路由器为例满足一对多的就叫做分发,分发请求的东西就是路由器
结合路由来看分发请求的对象就是路由
简单路由代码实现
<body>
<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>
<script src="src/index.js"></script>
</body>
// 获取用户想去哪里
let number = window.location.hash.substring(1);
// 获取界面
let div = document.querySelector(`#div${number}`);
// 渲染界面
if (div) div.style.display = "block";
let app = document.querySelector("#app");
// 展示界面
app.appendChild(div);
// 监听路由的改变实现页面重新渲染
window.addEventListener("hashchange", e => {
let number = window.location.hash.substring(1);
let div = document.querySelector(`#div${number}`);
if (div) div.style.display = "block";
let app = document.querySelector("#app");
app.children[0].style.display = "none";
document.body.appendChild(app.children[0]);
app.appendChild(div);
});
上面的#1、#2就叫做hash
路由分类:默认路由、保底路由、嵌套路由
默认路由:上面的页面如果我们不给#就会报错,这时候我们可以设置number= number || 1,这个一进去的页面也就是路由后面没有任何参数的
保底路由(404路由)
上面的代码如果我们后面的hash不是#1这些,而是#ccc就会报错,这时候我们就应该判断如果div不存在就让我们固定的一个div显示出来
if (div) {
div.style.display = "block";
} else {
div = document.querySelector("#div404");
div.style.display = "block";
}
代码优化
function route() {
let app = document.querySelector("#app");
let number = window.location.hash.substring(1);
number = number || 1;
let div = document.querySelector(`#div${number}`);
console.log(div);
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", e => {
route();
});
路由表
上面的代码中我们的代码通过number来获取div对应的,比如number是1,我们就查找div1,那么我们凭什么要找div1,为什么不是one1那,所以我们需要通过一个表结构来自定义hash值跟div的对应关系
const div1 = document.createElement('div')
div1.innerHTML = '1'
const div2 = document.createElement('div')
div2.innerHTML = '2'
const div3 = document.createElement('div')
div3.innerHTML = '3'
const div4 = document.createElement('div')
div4.innerHTML = '4'
const routeTable = {
'1': div1,
'2': div2,
'3': div3,
'4': div4
}
function route() {
let app = document.querySelector("#app");
let number = window.location.hash.substring(1);
number = number || 1;
let div = routeTable[number.toString()]
if (!div) {
div = document.querySelector("#div404");
}
app.innerHTML = ''
app.appendChild(div);
}
route()
window.addEventListener('hashchange', () => {
route()
})
这样就能很明显的看出number和div之间的对应关系了
路由的三种模式
1. hash模式
适用场景:任何情况下都能做前端路由
缺点:SEO不友好(服务器收不到hash,也就是说浏览器是不会把#之后的内容发给服务器的)
比如我们输入www.baidu.com/#aaa,当我们打开控制台查看请求的时候会发现请求的url依旧是www.baidu.com,但实际上我们的baidu.com/#aaa和baidu.com里面的内容是不同的,所以对我们的seo很不友好
2. history模式
适用场景:后端将所有的前端路由都渲染到同一个页面(不包括404页面)
缺点:IE8以下不支持
比如:我不管输入baidu.com还是baidu.com/aaa都能访问首页
代码实现:
将之前的代码中的#改成/,然后通过window.location.pathname来获取它的路径
const div1 = document.createElement("div");
div1.innerHTML = "1";
const div2 = document.createElement("div");
div2.innerHTML = "2";
const div3 = document.createElement("div");
div3.innerHTML = "3";
const div4 = document.createElement("div");
div4.innerHTML = "4";
const routeTable = {
"/1": div1,
"/2": div2,
"/3": div3,
"/4": div4
};
function route() {
let app = document.querySelector("#app");
let number = window.location.pathname;
if (number === '/') {
number = '/1'
}
let div = routeTable[number.toString()];
if (!div) {
div = document.querySelector("#div404");
}
div.style.display = "block";
app.innerHTML = "";
app.appendChild(div);
}
route();
window.addEventListener("hashchange", () => {
route();
});
问题:每次路径变的时候页面都会重新刷新渲染体验很差
解决方法使用history这个api
<a href="/1" class="link">go to 1</a>
<a href="/2" class="link">go to 2</a>
<a href="/3" class="link">go to 3</a>
<a href="/4" class="link">go to 4</a>
<div id="app"></div>
const allA = document.querySelectorAll("a.link");
for (let a of allA) {
a.addEventListener("click", e => {
e.preventDefault();
const href = a.getAttribute("href");
window.history.pushState(null, `page${href}`, href);
onStateChange();
});
}
function onStateChange() {
route();
}
route();
上面的代码先获取所有的跳转页面对应的a标签,然后在点击他们的时候阻止默认的跳转行为然后通过属性拿到他们里面的路径,在通过history将对应的路径push进去,每次push完后重新渲染页面,就可以让页面不刷新了
完整代码https://codesandbox.io/embed/strange-wood-c54y7
3. memory模式
既不用hash又不用history,把路径存在用户看不见的地方(比如localStorage)
缺点:只对单机有效
function route() {
let app = document.querySelector("#app");
let number = window.localStorage.getItem("xxx");
if (!number) {
number = "/1";
}
let div = routeTable[number.toString()];
if (!div) {
div = document.querySelector("#div404");
}
div.style.display = "block";
app.innerHTML = "";
app.appendChild(div);
}
const allA = document.querySelectorAll("a.link");
for (let a of allA) {
a.addEventListener("click", e => {
e.preventDefault();
const href = a.getAttribute("href");
window.localStorage.setItem("xxx", href);
onStateChange();
});
}
function onStateChange() {
route();
}
route();
三种模式的区别
hash和history模式都是把路径存在url上面,只要是不放在url上面的就是memory,memory因为没有对应的url,所以只对单机有效,不可分享;而hash和history可以通过url记录你的路由,所以是可分享的路由