你們都知道,經過瀏覽器的地址欄來改變切換頁面,前端實現主要有兩種方式:javascript
①經過hash改變,利用window.onhashchange 監聽。前端
②經過history的改變,進行js操做加載頁面,然而history並不像hash那樣簡單,由於history的改變,除了瀏覽器的幾個前進後退(使用 history.back(), history.forward()和 history.go() 方法來完成在用戶歷史記錄中向後和向前的跳轉。)等操做會主動觸發popstate 事件,pushState,replaceState 並不會觸發popstate事件,本篇文章主要解決history監聽的問題,下面來看下具體實現java
咱們首先完成一個訂閱-發佈模式,而後重寫history.pushState, history.replaceState,並添加消息通知,這樣一來只要history的沒法實現監聽函數就被咱們加上了事件通知,只不過這裏用的不是瀏覽器原生事件,而是經過咱們建立的event-bus 來實現通知,而後觸發事件訂閱函數的執行。 廢話很少說,下面咱們來作具體操做。瀏覽器
class Dep { // 訂閱池
constructor(name){
this.id = new Date() //這裏簡單的運用時間戳作訂閱池的ID
this.subs = [] //該事件下被訂閱對象的集合
}
defined(){ // 添加訂閱者
Dep.watch.add(this);
}
notify() { //通知訂閱者有變化
this.subs.forEach((e, i) => {
if(typeof e.update === 'function'){
try {
e.update.apply(e) //觸發訂閱者更新函數
} catch(err){
console.warr(err)
}
}
})
}
}
Dep.watch = null;
class Watch {
constructor(name, fn){
this.name = name; //訂閱消息的名稱
this.id = new Date(); //這裏簡單的運用時間戳作訂閱者的ID
this.callBack = fn; //訂閱消息發送改變時->訂閱者執行的回調函數
}
add(dep) { //將訂閱者放入dep訂閱池
dep.subs.push(this);
}
update() { //將訂閱者更新方法
var cb = this.callBack; //賦值爲了避免改變函數內調用的this
cb(this.name);
}
}
複製代碼
下面咱們只須要對history的方法進行重寫,並添加event-bus便可,代碼以下:app
var addHistoryMethod = (function(){
var historyDep = new Dep();
return function(name) {
if(name === 'historychange'){
return function(name, fn){
var event = new Watch(name, fn)
Dep.watch = event;
historyDep.defined();
Dep.watch = null; //置空供下一個訂閱者使用
}
} else if(name === 'pushState' || name === 'replaceState') {
var method = history[name];
return function(){
method.apply(history, arguments);
historyDep.notify();
}
}
}
}())
window.addHistoryListener = addHistoryMethod('historychange');
history.pushState = addHistoryMethod('pushState');
history.replaceState = addHistoryMethod('replaceState');
複製代碼
上面咱們給window添加了一個addHistoryListener事件監聽,相似於 addEventListener的方法,而後咱們有作了history的pushState, replaceState的改寫,接下來咱們測試一下。函數
window.addHistoryListener('history',function(){
console.log('窗口的history改變了');
})
window.addHistoryListener('history',function(){
console.log('窗口的history改變了-我也聽到了');
})
history.pushState({first:'first'}, "page2", "/first")
複製代碼
觀察上面結果打印;咱們發現window的 history改變,咱們成功的添加了事件監聽!固然這裏仍是有缺陷的,就是少了事件的移除,有興趣的同窗能夠把接下來的移除也書寫一下,熟悉熟悉。測試