1. 程式人生 > >利用pushState, popState和location.hash等方法自己實現一個小型路由

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

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

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

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

其中pushState:

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

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

接收url的引數為string型別,用以改變當前位址列的url.需要注意的一點就是這個引數不能和跨域,即協議,域名,埠必須都是相同的,如果出現跨域的情況,即會提示:

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:

    
    開啟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:

    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. 可進行按需載入...

等等一些實用的好處吧

相關推薦

利用pushState, popStatelocation.hash方法自己實現一個小型路由

這篇文章主要是記錄下HTML5中history提供的pushState, replaceStateAPI。最後通過這些API自己實現小型的路由。 關於window.history提供的API請參見Mozilla文件 其中history提供的pushState和replace

stub skeleton 的講解,自己實現一個stubskeleton程式

RMI的本質就是實現在不同JVM之間的呼叫,它的實現方法就是在兩個JVM中各開一個Stub和Skeleton,二者通過socket通訊來實現引數和返回值的傳遞。      有關RMI的例子程式碼網上可以找到不少,但絕大部分都是通過extend the interface java.rmi.Remote實現,

利用iframelocation.hash實現跨域

       原理是利用location.hash來進行傳值。在url: http://a.com#helloword中的‘#helloworld’就是location.hash,改變hash並不會導致頁面重新整理,所

關於利用HashSet,split,deleteCharAt方法詳解

1.首先了解一下HashSet的原理: Set介面  Set是對數學上集的抽象,Set中不包含重複的元素.如何界定是否是重複元素?Set最多可含一個null元素;對於任意的非null元素e1和e2,都滿足e1.equals(e2)==false.  Object.hashcode()的約定:a.在程式的一次執

jQuery中利用keyup事件判斷是否為漢字來實現動態搜尋

需求:有時候在做介面搜尋功能的時候,會想要根據使用者輸入的漢字來檢索出相應的內容。要求是動態的檢索,使用者每次輸入一個字或一個詞就會進行關鍵字模糊搜尋。 實現方法:如果直接使用文字框的keyup()事

Java用類物件,構造方法實現超市管理系統

1.用自己的思維模式構建了2個類(User類和Account類)來實現。 2.根據馮經理的思維多建立了幾個方法(login(),initDate(),search(),delete(),),發現把很多功能模組寫成方法會使程式可讀性更強 • 遇到的問題和解決方案; 問題:

java中有幾種方法可以實現一個執行緒?用什麼關鍵字修飾同步方法 stop()suspend()方法為何不推薦使用?

java5以前,有兩種實現方法,分別使用new Thread()和new Thread(runnable)形式,第一種繼承Thread類,直接呼叫thread的run方法,所以,我們往往使用Thread子類,即new SubThread()。第二種是實現Runn

歸併排序--自上而下自下而上兩種方法實現

歸併排序思想 自上而下的遞迴 對一個數組(str)選中一箇中間位置(mid=(start+end)/2),分別進行左遞迴(mergeSort(str,start,mid,length)),右遞迴(mergeSort(str,mid+1,end,length)),在回朔的時候

java中有幾種方法可以實現一個執行緒?用什麼關鍵字修飾同步方法? stop()suspend()方法為何不推薦使用?

java5以前,有如下兩種: 第一種: new Thread(){}.start();這表示呼叫Thread子類物件的run方法,new Thread(){}表示一個Thread的匿名子類的例項物件,子類加上run方法後的程式碼如下: new Thread(){ publi

不使用JDK的方法自己實現字符串轉整數

for parse 使用 har trim ++ exception int char 暫未考慮正負符號的情況。 public static int parseInt(String str) { if (str == null || str.tri

python只使用QueueThread自己實現一個最簡單的執行緒池

        我的思路就是就是寫一個TifCutting類繼承自Thread,這個類裡有個屬性Queue;有一個addTask新增任務的方法,這個方法是把需要執行的函式放到Queue裡;因為繼承自Thread類,一定有一個重寫的run方法,這個方法是從自己的Queue屬性裡

利用HashMap,自己實現一個簡易版的HashSet

簡介 HashSet是常用的容器類,主要特徵表現為不可重複性 其內部實現主要用到了HashMap,利用了HashMap的鍵的不可重複性 使用HashMap的鍵作為其元素,同時令HashMap的所有的

講解tf.estimator.Estimator tf.layers高階API實現一個CNN

tf.contrib.layers.flatten 假設輸入(inputs)的第一個維度表示batch_size。在保持batch_size的同時,使輸入的shape變為: [batch_size, k] tf.contrib.layers.flatten( inputs,

Quartz 之 scheduler 類的方法實現一個 quartz 管理類】

pause : 暫停一個觸發器。如果是持久化的 quartz,此觸發器的狀態會被寫到庫中,哪怕是重啟應用後, 也不會觸發,因為狀態是持久化的。 若更新 quartz 配置檔案中的該觸發器的屬性,如 cron 表示式,則記憶體和資料庫中已持久化的資料都不會被更新。 res

netty原始碼解解析(4.0)-20 ChannelHandler: 自己實現一個自定義協議的伺服器客戶端

  本章不會直接分析Netty原始碼,而是通過使用Netty的能力實現一個自定義協議的伺服器和客戶端。通過這樣的實踐,可以更深刻地理解Netty的相關程式碼,同時可以瞭解,在設計實現自定義協議的過程中需要解決的一些關鍵問題。   本週章涉及到的程式碼可以從github上下載: https://git

div+css 兼容ie6 ie7 ie8 ie9FireFox Chrome瀏覽器方法(非原創)

需要 通過 是我 point 一定的 oba 對象 important not div+css 兼容ie6 ie7 ie8 ie9和FireFox Chrome等瀏覽器方法 1.DOCTYPE 影響 CSS 處理 2.FF: div 設置 margi

Python: 字符串搜索匹配,re.compile() 編譯正則表達式字符串,然後使用match() , findall() 或者finditer() 方法

nth post cde clas import 預編譯 正則 一次 find 1. 使用find()方法 >>> text = ‘yeah, but no, but yeah, but no, but yeah‘ >>> text.fi

Django通用檢視的get_queryset, get_context_dataget_object方法

Django通用檢視的get_queryset, get_context_data和get_object等方法 簡介: Django提供了很多通用的基於類的檢視(class Base View),可以幫我簡化以下的程式碼操作。 這些基類的檢視還提供了get_queryset()

putty終端遠端登入Ubuntu 'ls'命令沒有顏色的解決方法(附 llla命令解決方法)

今天在一臺新的電腦上下載了putty工具遠端登入了下Ubuntu伺服器,執行了 ‘ls’ 命令,竟然列出的檔案或目錄都同一顏色,體驗相當不好: 解決方法,只要修改下~/.bash_profile檔案就可以了,在檔案中新增命令:alias ls=’ls --colo