利用pushState, popState和location.hash等方法本身實現一個小型路由

這篇文章主要是記錄下HTML5中history提供的pushState, replaceStateAPI。最後經過這些API本身實現小型的路由。javascript

關於window.history提供的API請參見Mozilla文檔html

其中history提供的pushStatereplaceState2個API提供了操做瀏覽器歷史棧的方法。前端

其中pushState:java

history.pushState(data, null, '#/page=1');
    
    pushState接收3個參數,第一個參數爲一個obj,表示瀏覽器
    
    第二個參數是document.title的值,通常設定爲`null`
    
    第三個參數string,用以改變 當前url

pushState方法在改變url的同時向瀏覽器歷史棧中壓入新的歷史記錄。git

接收url的參數爲string類型,用以改變當前地址欄的url.須要注意的一點就是這個參數不能和跨域,即協議,域名,端口必須都是相同的,若是出現跨域的狀況,即會提示:github

Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'http://www.baidu.com/' cannot be created in a document with origin 'http://commanderXL.com' and URL

Example:web

打開www.baidu.com

    history.pushState(null, null, '?page=1')
    //地址欄變成 www.baidu.com/?page=1
    
    history.pushState(null, null, '#page=2');
    //地址欄變成 www.baidu.com/#page=2

其中replaceState:ajax

history.replaceState(null, null, '#page=2');

replaceState接收的參數pushState相同,可是最終的效果是:地址欄url會根據接收的參數而變化,可是瀏覽器並未在當瀏覽歷史棧中增長瀏覽器的歷史記錄,而是替換當前的瀏覽器歷史記錄。跨域

經過pushStatereplaceState雖然能改變URL,可是不會主動觸發瀏覽器reload瀏覽器

window對象還提供popstate方法:

window.addEventListener('popstate', function() {
        
    });

這個方法用以監聽瀏覽器在不一樣歷史記錄中進行切換,而觸發相應的事件。

在瀏覽器提供的history對象上還有go, back方法,用以模擬用戶點擊瀏覽器的前進後退按鈕。在某個web應用當中,好比點擊了<a>標籤,發生了頁面的跳轉。這時調用history.back();方法後頁面回退,同時頁面發生刷新,這時window.onpopstate沒法監聽這個事件。可是若是是經過pushState或者replaceState來改變URL且不發生瀏覽器刷新的話,再使用history.back()history.go(),這樣popstate事件會被觸發。

history.pushState({page: 1}, null, '?page=1');
    history.pushState({page: 2}, null, '?page=2');

    history.back(); //瀏覽器後退

    window.addEventListener('popstate', function(e) {
        //在popstate事件觸發後,事件對象event保存了當前瀏覽器歷史記錄的狀態.
        //e.state保存了pushState添加的state的引用
        console.log(e.state);  //輸出 {page: 1}
    });

PS: 經過pushState在url上添加?page=1能夠經過location.search去獲取search的內容。不過若是經過location.search去改變url的話是會主動觸發瀏覽器reload的。這個特性能夠和下面將的關於hash的內容對比下。

API大體瞭解了,那麼這些方法能夠運用到哪些地方呢?一個比較經常使用的場景是就在單頁應用中,經過這些API完成前端的路由設計,利用pushState, replaceState能夠改變url同時瀏覽器不刷新,而且經過popstate監聽瀏覽器歷史記錄的方式,完成一系列的異步動做。

<a data-href="/post"></a>
    <a data-href="/login"></a>
    
    //路由
    const Router = [];
    
    const addRoute = (path = '', handle = () => {}) => {
        let obj = {
            path,
            handle
        }
        
        Router.push(obj);
    }
    
    
    //添加路由定義
    addRoute('/post', function() {
        //do something
    });
    
    addRoute('/login', function() {
        //do something
    })
    
    
    //路由處理
    const routeHandle = (path) => {
        Router.forEach((item, index) => {
            if(item.path === path) {
                item.handle.apply(null, [path]);
                return true;
            }
        })
        return false;
    }
    
    
    //攔截默認的a標籤行爲
    document.addEventListener('click', function(e) {
        let dataset = e.target.dataset;
        if(dataset) {
            if(routeHandle(dataset.href)) {
                //阻止默認行爲
                e.preventDefault();
            }
        }
    })

大體的實現思路就是,經過<a>添加路由信息,而後攔截<a>標籤的默認行爲,並與註冊的路由信息進行匹配。若匹配成功調用對應的handle方法.

不過pushStatereplaceState方法在低版本的IE瀏覽器下兼容性不是很好。因此能夠進行降級使用hash來進行路由設計。

hash請戳我

能夠經過location.hash獲取url上第一個#(fragment)及後面的內容。同時還能經過location.hash改寫其內容,且不會主動觸發瀏覽器reload。 有些功能是否是和pushStatereplaceState同樣? 因此爲了兼容到低版本的瀏覽器,能夠經過監聽#變化來進行路由設計。

那麼如何去監聽呢? 比較粗暴的一種方式就是polling

var oldHash = location.hash;
    setTimeInterval(function() {
        if(oldHash !== location.hash) {
            
            //do something
        
            oldHash = location.hash;
        }
    }, 100);

不過,H5還提供了一個API: hashchange。它的就能夠直接代替上面的polling方法,來監聽#的變化。

window.addEventListener('hashchange', function() {
        routeHandle(locaiton.hash);
    });

這個小型的路由設計能夠參見個人github.

稍微總結下:

上面主要介紹了history提供的一些API,hash的相關知識。在平時能夠運用到SPA當中,Gmail就是經過hash來進行路由設計的。它相對於頁面跳轉來講:

  1. 頁面只須要加載一次。後面的頁面切換能夠經過ajax去請求數據。頁面體驗更加流暢;

  2. 能夠利用本地緩存,優化頁面體驗。在不一樣頁面切換的過程當中更加流暢;

  3. 可進行按需加載...

等等一些實用的好處吧。

項目地址

項目地址請戳我

相關文章
相關標籤/搜索