hash模式
hash就是指url尾巴后的#号以及后面的字符,由于hash值的变化不会导致浏览器向服务器发出请求,而且hash改变会触发onhashchange事件。
hash的特点:
hash虽然出现url中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。hash本来拿来做页面定位的,如果拿来做路由的话,原来锚点功能就不能用了。hash模式又叫做前端路由,因为改变hash值不会向后端发送请求
window.location.hash='qq'//设置 url 的 hash,会在当前url后加上'#qq'
var hash = window.location.hash //'#qq'
window.addEventListener('hashchange',function(){
//监听hash变化,点击浏览器的前进后退会触发
})
history模式
history不会在URL中出现#号,与HTTP保持一致以/开头,所以当history发生改变时会重新加载页面。
想要不刷新页面的改变路由history提供了两个APLI(利用pushState()和replaceState()方法来修改pathame,不会刷新页面)--这个两个方法接收三个参数;stateObj,title,url
history模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中。
history也可以直接改变URL但是会刷新页面
widow.location.pathname ='/first' //设置URL的history值为‘/first’
var history = window.location.history //'/first'
history API
- 切换历史状态包括back()后退、foward()前进、go()跳转,会在浏览记录中前进后退,如果超出,会使调用的方法无效果
history.back(); /后退一次
history。forward(); //前进一次
history.go(); //刷新当前页面
history.go(-2); //后退两次
history.go(2); //前进两次
注意:移动到以前访问过的页面时,页面通常是从浏览器缓存中加载,而不是重新要求服务器发送新的网页。
2. 修改历史状态包括pushState,replaceState
window.history.puahState(stateObj,title,url);
//state: 需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
//title:标题,现在基本上没用,一般传null或者”“
//url:设定新的历史记录的url,新的url与当前url的origin必须是一样的(同一个域),否则会出错误,浏览器地址栏会显示这个新网址
实例1
如果当前网址为www.com/first,用history.pushState()方法在浏览历史中添加一个新记录
window.history.pushState({first;1},null,'second');
浏览器地址栏会立刻显示为www.com/second,但是不会跳转到second页面,甚至不会检查second是否存在,当访问一个新地址(baidu.com),回退一次页面url将显示second,再退一次会显示first
实例2
如果当前url是www.com/a/
window.history.pushState(null,null,'./qq/')
url会变成:www.com/a/qq/ //前面一个路由必须以”/“结尾,否则不会拼接
window.history.replaceState(stateObj,title,url);
//与pushState参数基本相同,但replaceState()是修改当前历史记录,而pushState()是创建新的历史记录
实例1
如果当前网页是:www.com
history.pushState(null,null,'/html.1');
//www.com/html.1
history.pushState(null,null,'/html.2');
//www.com/html.2
history.replaceState(null,null,'html.3');
//www.com/html.3 replaceState会替换刚刚那个pathname
history.back(); //后退一次
//www.com/html.1
history.back(); //后退一次
//www.com
history.go(2) //前进两次
//www.com/html.3
- popstate事件
当浏览历史(history对象)出现变化时,就会触发popstate事件,但pushState()方法和replaceState()方法不会触发该事件,只有当用户点击浏览器倒退和前进按钮,或者使用history.back()、history.forward()、history.go()方法时才会触发。该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。
window.addEventListener('popstate',cunction(){
console.log(111);
})
history.back(): //111
监听history的变化
其实大多时候,我们都想改变URL且不让浏览器发出请求,但在history里面,调用pushState()和replaceState()会改URL且不刷新页面,但是没有监听的他们的方法,就不能监听到路由的变化执行相应的js。
history不像hash那样有onhashchange监听,所以我们可以自己写个监听事件,先完成一个订阅-发布模式,然后重写history.puahState和history.replaceState并添加消息通知。
class Store { //订阅池
constructor(name) {
this.store = [] //该事件下被订阅的对象集合
}
bind() { //添加订阅者
Store.watch.add(this)
}
dispatch() { //通知订阅者有变化
this.store.forEach((item) => {
if (typeof item.update === 'function') { //判断store对象里面对应事件是否是个方法
item.update.apply(item) //触发订阅者更新函数
}
})
}
}
Store.watch = null; //创建一个空状态的订阅者
class Watch {
constructor(name, fn) {
this.name = name; //订阅消息的名称
this.callBack = fn; //订阅消息发生改变时->订阅者执行的回调函数
}
add(dep) { //将订阅者放入订阅池
dep.store.push(this);
}
update() { //将订阅者更新方法
var cb = this.callBack;//赋值为了不改变函数内调用的this
cb(this.name);
}
}
//改写pushState和replaceState方法
var addHistoryMethod = (function () { //自执行函数返回return的结果,如果是函数就返回函数
var historyStore = new Store();
return function (name) { //通过historyMethod()传入一个name
if (name === 'historychange') { //判断调用了什么方法,又返回一个函数
return function (name, fn) {
var event = new Watch(name, fn) //把Watch实例给event
Store.watch = event; //把把实例给Store.watch以便在bind里面调用add方法
historyStore.bind();
Store.watch = null; //置空供下一个订阅者使用
}
} else if (name === 'pushState' || name === 'replaceState') {
var method = window.history[name]; //history.name
return function () {
method.apply(window.history, arguments);//history表示指向的this为history,arguments获取传递过来的实参
historyStore.dispatch();
}
}
}
}())
//设置监听样式
window.addHistoryListener = addHistoryMethod('historychange');
window.history.pushState = addHistoryMethod('pushState');
window.history.replaceState = addHistoryMethod('replaceState');
调用监听history
window.history.pushState(null,null,'/second')
window.addHistoryListener('history',function() {
console.log('history改变了')
})