聊聊React Router中的History

2021-09-14 03:14:20 字數 2489 閱讀 3196

在學習react router時,看到有關history有如下描述:

react router 是建立在 history 之上的。 簡而言之,乙個 history 知道如何去監聽瀏覽器位址列的變化, 並解析這個 url 轉化為 location 物件, 然後 router 使用它匹配到路由,最後正確地渲染對應的元件。
這段描述看的暈暈的,history到底是個什麼東西呢?這得從history api的出身說起。

我們都知道乙個url代表了網路上唯一的乙個資源。這個資源可以是乙個頁面,一張等等。在位址列裡輸入乙個url位址,瀏覽器就會將對應的資源展示出來。當在不同的位址之間跳轉時,我們很自然地想要回退或者前進乙個位址。為了實現這個功能,瀏覽器廠商定義了history物件。這時的history物件大致有go()forward()back()等方法,用於實現歷史記錄的訪問、跳轉。

使用 history api與瀏覽器歷史記錄進行互動
history與 url 就這樣和諧的相處了一段時間,直到ajax技術的興起。

瀏覽器有乙個限制:如果你改變了url,甚至是通過指令碼,它就得向伺服器傳送請求,重新整理整個頁面。這很耗時,也耗資源,因為有時候2個頁面長得差不多,僅僅有一小塊地方不同。為了解決這個問題,ajax技術興起,利用ajax,可以不改變url,只向伺服器請求需要變動的資料,然後在前端通過dom操作實現頁面的區域性更新。

ajax極大提高了頁面載入速度與使用者體驗。不過它同時帶來了乙個問題:同乙個url位址,可能會展示很多不同的資訊,那麼它作為唯一資源識別符號的這個定義,在這裡被破壞了。

有沒有乙個兩全其美的辦法呢?既可以實現頁面的區域性更新,又能夠改變位址列裡的url?

井號方案

第乙個跳出來解決問題的是url中的##代表網頁中的乙個位置。它右邊的字元就是頁面中的位置識別符號。例如下面的url:

就代表網頁index.html的print位置。瀏覽器讀取這個url後,會自動將print位置滾動至對應的區域。為網頁位置指定識別符號,有兩個方法。一是使用錨點,比如,二是使用id屬性,比如

#的威力在於它是用來指導瀏覽器動作的,對伺服器端完全無用。所以,http請求中不包括#,改變#不觸發網頁過載。因此,如果我們在使用ajax區域性重新整理頁面時,同時改變url#後面的識別符號,就實現了區域性重新整理+改變url了。

事實上,早前的twitter、google就是這麼實現的。google甚至為此專門定義了乙個搜尋引擎優化的識別符號#!。可以參考這篇文章:url的井號

history方案

url畢竟是用於資源分享的,帶個#總感覺有點彆扭。如果不用#,有沒有辦法能夠修改url,同時又不向伺服器傳送請求呢?這就輪到history物件再次登場了。在html5規範中,w3c對history物件進行了一波公升級:

html5 history api包括2個方法:history.pushstate()和history.replacestate(),以及1個事件:window.onpopstate。
通過指令碼傳送ajax請求到伺服器,獲取伺服器響應資料

根據響應的資料操作dom,更新頁面內容

利用history.pushstate()或replacestate()方法,修改位址列中的url

問題得到解決。

在react router中,將上述2個方案進行了整合,統一到了乙個history庫中。也就是我們看到的hashhistorybrowserhistoryhashhistory是上面井號方案的封裝,browserhistory則是對history方案的封裝。特別需要注意到是,browserhistory需要對伺服器端改造。

為什麼?

/welcome

/detail

如果我們在瀏覽器裡直接輸入主頁面位址,然後通過頁面裡鏈結跳轉到二級頁面,一般是沒有問題的。但是,如果在瀏覽器裡直接輸入/welcome會發生什麼呢?

瀏覽器會認為這是乙個url請求,向伺服器端請求這個url代表的資源。但我們這是乙個單頁面應用,服務端只有乙個index.html頁面,沒有跟這個位址匹配的資源,頁面就會報錯了。

因此,我們需要稍微改造一下服務端,將所有這樣的請求都指向index.html頁面,由前台處理路由資訊。

#有話說:

為嘛,長得醜就要被淘汰嗎!

react router在ts中的宣告使用

當我們使用react router進行路由跳轉的時候,我們通常都會傳遞一些引數例如 param,state,query 目前已經被廢棄 但是結合typescript使用時,我們需要宣告元件的props型別,否則的話我們的tslint就會阻止程式的繼續執行,接下來我就介紹一下在react中元件的ts宣...

聊聊javascript中的陣列

定義陣列 1.var 陣列名稱 元素1,元素2,var arr 定義空陣列 var arr1 100,前端 true,undefined 定義陣列同時新增不同型別的元素2.建構函式方式 var 陣列名稱 new array 元素1,元素2,var 陣列名稱 new array length leng...

聊聊golang中的鎖

學習過作業系統的都知道程式有臨界區這個概念,臨界區就是程式片段訪問臨界資源的那部分 臨界區同一時刻只能有乙個執行緒進行訪問,其他執行緒需要訪問的話必須等待資源空閒。那麼一般程式語言都會使用鎖來進行臨界區訪問控制。golang主要有兩種鎖 互斥鎖和讀寫鎖 互斥鎖mutex mutex 用於提供一種加鎖...