hash | history |
---|---|
#后的就是hash内容 | history即正常的路径 |
可以通过location.hash拿到 | location.pathname |
可以通过onhashchange监听hash的改变 | 可以用onpopstate监听history的变化 |
hash 模式:
#后面 hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面
通过监听 hashchange 事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。
history 模式:
history 模式的实现,主要是 HTML5 标准发布的两个 API,pushState 和 replaceState,这两个 API 可以在改变 url,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作。
区别:
url 展示上,hash 模式有“#”,history 模式没有
刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由
兼容性,hash 可以支持低版本浏览器和 IE。
前端路由中,实现路由的2种机制:history hash
URL后 #1 hash
路由变化:
window.location.hash = 1
Let hash = window.location.hash
window.onhashchange = function (e) {
// 可捕捉URL变化,可实现spa
console.log(‘旧的’, e.oldURL)
console.log(‘新的’, e.newURL)
}
浏览器中1改为2,oldURL = 1, newURL = 2
返回键:old URL = 2, newURL = 1
· Hash机制思路(下)兼容性好,可兼容ie8
1 创建路由对象,register处理每个hash对应的回调函数
2 不存在hash时,默认首页,实现index方法,处理首页的回调(不点击任何链接时)
3 通过hashchange监听hash的变化,定义hash变化的回调函数
<!DOCTYPE html>
<html>
<head>
<title>SPA路由原理与实现方式:hash机制</title>
</head>
<body>
<div id="nav">
<a href="#/page1">page1</a>
<a href="#/page2">page2</a>
<a href="#/page3">page3</a>
</div>
<div id="container"></div>
</body>
<script type="text/javascript">
/*
* 1. 创建一个路由对象,register用于处理每个hash值对应的回调函数
* 2. 不存在hash的时候,默认首页。实现一个index方法,用于处理首页的回调函数
* 3. 通过hashchange事件来监听hash的变化,定义hash变化的回调函数
*/
function Router () {
// 用于存储不同的hash值对应的回调函数
this.routes = {};
// 监听hashchange
window.addEventListener('hashchange', this.reload.bind(this));
}
// 首页的回调方法
Router.prototype.index = function (callback) {
this.routes['index'] = callback;
}
// 其他页面的回调方法
Router.prototype.register = function (hash, callback) {
this.routes[hash] = callback;
}
// 实现reload方法
Router.prototype.reload = function () {
let callback;
// /page3
let hash = location.hash.slice(1);
!hash ? callback = this.routes.index : callback = this.routes[hash]
callback.call(this)
}
// 调用
let router = new Router();
let container = document.getElementById('container');
// 首页的回调函数
router.index(() => {
container.innerHTML="我是首页";
})
// 回调函数
router.register('/page1', ()=>{
container.innerHTML="我是page1";
})
router.register('/page2', ()=>{
container.innerHTML="我是page2";
})
router.register('/page3', ()=>{
container.innerHTML="我是page3";
})
// 调用
router.reload();
</script>
</html>
History 后退:history.go(-1)。history.go(2) 简单的页面跳转
H5新增的两个api:
History.pushstate() 跳转URL且能后退
history.replacestate() 替换URL且不能回退
可改变URL,不会刷新页面,具备实现前端路由的能力,不会出发任何事件,暂时无法监听。history.popstate监听history下的路由变化
变向监听,url可能改变的情况:
1 浏览器前进,后退
2 a标签
3 js中,触发history.replacestate() 和 history.pushstate()
<!DOCTYPE html>
<html>
<head>
<title>SPA路由原理与实现方式:history机制</title>
</head>
<body>
<div id="nav">
<a href="/page1">page1</a>
<a href="/page2">page2</a>
<a href="/page3">page3</a>
</div>
<div id="container"></div>
</body>
<script type="text/javascript">
/*
* 1. 创建一个路由对象,register location.pathname的值对应回调函数
* 2. 处理首页
* 3. 处理其他页
es5的写法:所有的方法挂在在原型上
es6的class 写法
*/
class Router {
constructor () {
// 用于存储不同的path值对应的回调函数
this.routes = {};
this.listenPopState()
this.listenLink()
}
// 监听postState,历史记录更改的时候,将触发postState事件
listenPopState () {
window.addEventListener('popstate', (e) => {
let state = e.state || {};
let path = state.path || ''
this.handle(path)
});
}
// a链接的页面跳转
listenLink () {
window.addEventListener('click', (e)=>{
let ev = e.target
if (ev.tagName === 'A' && ev.getAttribute('href')) {
e.preventDefault()
// 更新URL
this.urlRefresh(ev.getAttribute('href'))
}
})
}
// load
load () {
let path = location.pathname
}
register (path, cb) {
this.routes[path] = cb
console.log(cb)
}
// 首页
index (cb) {
this.routes['/'] =cb
}
// 未找到路由
notfound (cb) {
this.routes['notfound'] = cb
}
// 出现异常
error (cb) {
this.routes['error'] = cb
}
// 更新URL
urlRefresh (path) {
// 参数,1合理的js对象,2 title 现在浏览器都不要写这个 3,url,跳转的界面
history.pushState({path}, null, path)
this.handle(path)
}
// 通用的回调函数
handle (path) {
let cb
cb = this.routes[path]
cb.call(this)
}
}
let router = new Router;
router.load()
let container = document.getElementById('container')
// 处理回调
// 首页的回调函数
router.index(() => {
container.innerHTML="我是首页";
})
router.register('/page1', ()=>{
container.innerHTML = '我是page1'
})
router.register('/page2', ()=>{
container.innerHTML = '我是page2'
})
router.register('/page3', ()=>{
container.innerHTML = '我是page3'
})
</script>
</html>