JavaScript如何實現history路由變化監聽

前言

你們都知道,經過瀏覽器的地址欄來改變切換頁面,前端實現主要有兩種方式: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方法,並添加window.addHistoryListener事件機制。

下面咱們只須要對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');

複製代碼

測試History事件監聽

上面咱們給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改變,咱們成功的添加了事件監聽!固然這裏仍是有缺陷的,就是少了事件的移除,有興趣的同窗能夠把接下來的移除也書寫一下,熟悉熟悉。測試

相關文章
相關標籤/搜索