react-router中history

目前, react 的生態愈來愈豐富,像 flux redux react-router 已經被愈來愈多的使用,本文就 react-router 的內部實現進行分析。文章主要包含兩大部分: 一是對 react-router 賴以依存的 history 進行研究;二是分析 react-router 是如何實現URL 與 UI 同步的。html

1. react-router的依賴基礎 – history

1.1 History的總體介紹

history 是一個獨立的第三方js庫,能夠用來兼容在不一樣瀏覽器、不一樣環境下對歷史記錄的管理,擁有統一的API。具體來講裏面的history分爲三類:html5

  • 老瀏覽器的history: 主要經過hash來實現,對應 createHashHistory
  • 高版本瀏覽器: 經過html5裏面的history,對應 createBrowserHistory
  • node環境下: 主要存儲在memeory裏面,對應 createMemoryHistory

上面針對不一樣的環境提供了三個API,可是三個API有一些共性的操做,將其抽象了一個公共的文件 createHistory :node

// 內部的抽象實現 function createHistory(options={}) {   ...   return {     listenBefore, // 內部的hook機制,能夠在location發生變化前執行某些行爲,AOP的實現     listen, // location發生改變時觸發回調     transitionTo, // 執行location的改變     push, // 改變location     replace,     go,     goBack,     goForward,     createKey, // 建立location的key,用於惟一標示該location,是隨機生成的     createPath,     createHref,     createLocation, // 建立location   } }

 

上述這些方式是history內部最基礎的方法, createHashHistory 、 createBrowserHistory 、 createMemoryHistory 只是覆蓋其中的某些方法而已。其中須要注意的是,此時的location跟瀏覽器原生的location是不相同的,最大的區別就在於裏面多了key 字段, history 內部經過 key 來進行 location 的操做。react

function createLocation() {   return {     pathname, // url的基本路徑     search, // 查詢字段     hash, // url中的hash值     state, // url對應的state字段     action, // 分爲 push、replace、pop三種     key // 生成方法爲: Math.random().toString(36).substr(2, length)   } }

 

1.2 內部解析

三個API的大體的技術實現以下:json

  • createBrowserHistory : 利用HTML5裏面的history
  • createHashHistory : 經過hash來存儲在不一樣狀態下的history信息
  • createMemoryHistory : 在內存中進行歷史記錄的存儲

1.2.1 執行URL前進

  • createBrowserHistory : pushState、replaceState
  • createHashHistory : location.hash=*** location.replace()
  • createMemoryHistory : 在內存中進行歷史記錄的存儲

代碼實現以下:redux

// createBrowserHistory(HTML5)中的前進實現 function finishTransition(location) {   ...   const historyState = { key };   ...   if (location.action === 'PUSH') ) {     window.history.pushState(historyState, null, path);   } else {     window.history.replaceState(historyState, null, path)   } } // createHashHistory的內部實現 function finishTransition(location) {   ...   if (location.action === 'PUSH') ) {     window.location.hash = path;   } else {     window.location.replace(     window.location.pathname + window.location.search + '#' + path   );   } } // createMemoryHistory的內部實現 entries = []; function finishTransition(location) {   ...   switch (location.action) {     case 'PUSH':       entries.push(location);       break;     case 'REPLACE':       entries[current] = location;       break;   } }

 

1.2.2 檢測URL回退

  • createBrowserHistory : popstate
  • createHashHistory : hashchange
  • createMemoryHistory : 由於是在內存中操做,跟瀏覽器沒有關係,不涉及UI層面的事情,因此能夠直接進行歷史信息的回退

僞代碼實現以下:api

// createBrowserHistory(HTML5)中的後退檢測 function startPopStateListener({ transitionTo }) {   function popStateListener(event) {     ...     transitionTo( getCurrentLocation(event.state) );   }   addEventListener(window, 'popstate', popStateListener);   ... }  // createHashHistory的後退檢測 function startPopStateListener({ transitionTo }) {   function hashChangeListener(event) {     ...     transitionTo( getCurrentLocation(event.state) );   }   addEventListener(window, 'hashchange', hashChangeListener);   ... } // createMemoryHistory的內部實現 function go(n) {   if (n) {     ...     current += n;   const currentLocation = getCurrentLocation();   // change action to POP   history.transitionTo({ ...currentLocation, action: POP });   } }

 

1.2.3 state的存儲

爲了維護state的狀態,將其存儲在sessionStorage裏面:瀏覽器

// createBrowserHistory/createHashHistory中state的存儲 function saveState(key, state) {   ...   window.sessionStorage.setItem(createKey(key), JSON.stringify(state)); } function readState(key) {   ...   json = window.sessionStorage.getItem(createKey(key));   return JSON.parse(json); } // createMemoryHistory僅僅在內存中,因此操做比較簡單 const storage = createStateStorage(entries); // storage = {entry.key: entry.state}  function saveState(key, state) {   storage[key] = state } function readState(key) {   return storage[key] }

 

2. react-router的基本原理

一句話:實現URL與UI界面的同步。其中在react-router中, URL 對應 Location 對象,而UI是由react components 來決定的,這樣就轉變成 location 與 components 之間的同步問題。服務器

react-router的實現原理

3. react-router的具體實現

react-router在 history 庫的基礎上,實現了URL與UI的同步,分爲兩個層次來描述具體的實現。session

3.1 組件層面描述具體實現過程

在 react-router 中最主要的 component 是 Router RouterContext Link , history 庫起到了中間橋樑的做用。

react-router的實現原理

3.2 API層面描述具體實現過程

爲了簡單說明,只描述使用browserHistory的實現,hashHistory的實現過程是相似的,就不在說明。

react-router的實現原理

4. 結語

目前 react-router 在項目中已有大量實踐,其優勢能夠總結以下:

  • 風格: 與React融爲一體,專爲react量身打造,編碼風格與react保持一致,例如路由的配置能夠經過component來實現
  • 簡單: 不須要手工維護路由state,使代碼變得簡單
  • 強大: 強大的路由管理機制,體如今以下方面
    • 路由配置: 能夠經過組件、配置對象來進行路由的配置
    • 路由切換: 能夠經過 <Link> Redirect 進行路由的切換
    • 路由加載: 能夠同步記載,也能夠異步加載,這樣就能夠實現按需加載
  • 使用方式: 不只能夠在瀏覽器端的使用,並且能夠在服務器端的使用

固然 react-router 的缺點就是API不太穩定,在升級版本的時候須要進行代碼變更。

相關文章
相關標籤/搜索