前言
- spa路由原理还是很有意思的,实际上是就是浏览器提供的api接口,但这个接口还有些缺陷,但是可以允许我们对其进行重新改写。
hash路由
- 哈希路由就是路由上面有个#,以前是用来做锚点的,很多人可能会想知道这个hash路由和锚点到底有啥区别?
- 锚点实际上是通过#后面的东西来跳到对应相同id的元素。
- 而哈希路由是监听这个哈希路由改变的事件,渲染相对应的页面元素。
- 那么问题来了,2者是同时起效还是有一个不起作用还是根本就冲突?
- 答案是都会起效。可以拿下面这个例子验证:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<a href="#/a">去/a</a>
<a href="#/b">去/b</a>
<div id="root"></div>
<div style="height: 1000px;"></div>
<div id="/b">231</div>
<script>
let root = document.getElementById('root');
window.addEventListener('hashchange', (event) => {
let hash = window.location.hash;
root.innerHTML = hash;
});
</script>
</body>
</html>
- 验证可以发现,页面跳向锚点231的同时,也把挂在root的元素给渲染出来了。
- 可能有人觉得有个
/
不是传统锚点写法,实际验证去了/
也是同时触发。
- 另外查看hash可以调用
window.location.hash
,如果给这个赋值,最开始的井号可以写也可以不写,对应的是一个哈希。也就是#/a
和/a
是一个路径,路径上都会写成#/a
。
browser路由
- browser路由主要是靠对window.history的操作。这个api有个特点,就是往history里push路由,会跳到相应路由,但没有监听函数,而回退操作却有监听函数。
- 由于没有push监听函数,所以可以对其进行改写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script>
let root = document.getElementById('root');
window.onpushstate = function (state, title, url) {
console.log({ type: 'onpushstate', state, pathname: url });
root.innerHTML = url;
};
(function (history) {
let pushState = history.pushState;
history.pushState = function (state, title, url) {
if (typeof window.onpushstate == 'function') {
window.onpushstate(state,title, url);
}
return pushState.apply(history, arguments);
}
})(window.history);
window.onpopstate = function (event) {
console.log({ type: event.type, state: event.state, pathname: window.location.pathname });
root.innerHTML = window.location.pathname;
}
setTimeout(() => {
window.history.pushState({ page: 1 }, 'page1', '/page1');
}, 1000);
setTimeout(() => {
window.history.pushState({ page: 2 }, 'page2', '/page2');
}, 2000);
setTimeout(() => {
window.history.pushState({ page: 3 }, 'page3', '/page3');
}, 3000);
setTimeout(() => {
window.history.go(-1);
}, 4000);
</script>
</body>
</html>
- 另外可以看见,window.history.pushState第一个参数时data,第二个参数是title,第三个是url,所以browserRouter可以携带状态信息去下一个路由,这样对权限判断之类提供了便捷,而这个地方也是跟哈希路由最大的区别。
- 当然可能有人会重写hash路由的方法让其带有信息,这就不在讨论范围内了。