spa 路由简易原理及其实现
浏览器 history 和 hash
hash
具体指的是浏览器url # 后面的内容,例如 https://www.google.com/#abc 的hash值为abc 浏览器会记录hash的变化,以至于前进后退可以使用
具体表现:
1 hash 变化可以随便改变更新url,不会引起服务器的更新,因为hash的变化并不会引起http之类的请求,所以hash可以频繁刷新
2 浏览器的onhashchange可以监听到hash的变化,从而去触发一些动作
html5 新的的两个hash方法
pushState()
replaceState
以上两者都可以改变url的地址 区别是前者可以增加history.length 后者则不会
虽然以上两者都可以改变url和页面标题,但是却不会让页面刷新和更改页面的展示,因为并没有触发onpopstate事件
popstate事件 只有触发了浏览器行为的时候(例如点击浏览器的前景和后退),就会触发popstate事件,其他的则不会
reload() replace() location 三者区别
location.reload() 取的是客户端缓存的页面
location.replace() 重新请求加载url指向的页面
location.href = url 等于pushstate修改url,会创建一条新的记录
简单 hash router 实现
<!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>
<script>
class Router {
constructor() {
this.routes = {};
this.currentUrl = '';
}
// routes来存放不同路由的回调函数
route (path, callback) {
this.routes[path] = callback || function() {};
}
updateView() {
this.currentUrl = location.hash.slice(1);
this.routes[this.currentUrl] && this.routes[this.currentUrl]();
}
// 初始化route,当load加载后更新页面
// 监听hashchange当hash改变时更新页面
init() {
window.addEventListener('load', this.updateView.bind(this), false);
window.addEventListener('hashchange', this.updateView.bind(this), false);
}
}
</script>
</head>
<body>
<div id='app'>
<ul>
<li>
<a href="#/">home</a>
</li>
<li>
<a href="#/about">about</a>
</li>
<li>
<a href="#/topics">topics</a>
</li>
</ul>
<div id='content'></div>
</div>
<script>
const router = new Router();
router.init();
router.route('/', function() {
document.getElementById('content').innerHTML = 'home';
});
router.route('/about', function () {
document.getElementById('content').innerHTML = 'about';
});
router.route('/topics', function () {
document.getElementById('content').innerHTML = 'topics';
});
</script>
</body>
</html>
简易 history 路由实现
hash 的改变可以出发hashchange事件,而history的改变并不触发事件,使得不能使用去监听history的改变而做出任何的改变
换个想法:引起url变化的原因无非有三个
1 浏览器的前进后退(可以通过popstate来监听)
2 a标签的点击
3 js代码history.pushState() 或者 history.replaceState()函数
简易实现如下:
<!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>
<script>
class Router {
constructor() {
this.routes = {};
this.currentUrl = '';
}
// routes来存放不同路由的回调函数
route(path, callback) {
this.routes[path] = callback || function () { };
}
updateView() {
this.currentUrl = location.hash.slice(1);
this.routes[this.currentUrl] && this.routes[this.currentUrl]();
}
bindLind() {
const alllink = document.querySelectorAll('a[data-href]');
for (let i = 0; i < alllink.length; i++) {
const current = alllink[i];
current.addEventListener(
'click',
e => {
e.preventDefault();
const url = current.getAttribute('data-href');
history.pushState({}, null, url);
this.updateView(url);
},
false
);
}
}
// 初始化route,当load加载后更新页面
// 监听popstate浏览器前进后退
init() {
// 该函数绑定a标签,并阻止默认行为
this.bindLind();
window.addEventListener('load', this.updateView.bind(this), false);
window.addEventListener('popstate', this.updateView.bind(this), false);
}
}
</script>
</head>
<body>
<div id='app'>
<ul>
<li>
<a data-href="#/">home</a>
</li>
<li>
<a data-href="#/about">about</a>
</li>
<li>
<a data-href="#/topics">topics</a>
</li>
</ul>
<div id='content'></div>
</div>
<script>
const router = new Router();
router.init();
router.route('/', function () {
document.getElementById('content').innerHTML = 'home';
});
router.route('/about', function () {
document.getElementById('content').innerHTML = 'about';
});
router.route('/topics', function () {
document.getElementById('content').innerHTML = 'topics';
});
</script>
</body>
</html>
小结: 有了对hash 和 history的基础了解后,再去研究当前流行的spa就非常容易了,简单来说原理就是通过hash的变化及其history的变化,来触发一系列的事件来更新页面。