react-router的實現原理

我的博客地址: 點擊這裏html

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

1. react-router依賴基礎 - history

1.1 History總體介紹

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

  • 老瀏覽器的history: 主要經過hash來實現,對應createHashHistoryreact

  • 高版本瀏覽器: 經過html5裏面的history,對應createBrowserHistorygit

  • node環境下: 主要存儲在memeory裏面,對應createMemoryHistorygithub

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

// 內部的抽象實現
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內部最基礎的方法,createHashHistorycreateBrowserHistorycreateMemoryHistory只是覆蓋其中的某些方法而已。其中須要注意的是,此時的location跟瀏覽器原生的location是不相同的,最大的區別就在於裏面多了key字段,history內部經過key來進行location的操做。redux

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的大體的技術實現以下:瀏覽器

  • createBrowserHistory: 利用HTML5裏面的history服務器

  • createHashHistory: 經過hash來存儲在不一樣狀態下的history信息

  • createMemoryHistory: 在內存中進行歷史記錄的存儲

1.2.1 執行URL前進

  • createBrowserHistory: pushState、replaceState

  • createHashHistory: location.hash=*** location.replace()

  • createMemoryHistory: 在內存中進行歷史記錄的存儲

僞代碼實現以下:

// 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層面的事情,因此能夠直接進行歷史信息的回退

僞代碼實現以下:

// 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來決定的,這樣就轉變成locationcomponents之間的同步問題。

圖片描述

3. react-router具體實現

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

3.1 組件層面描述實現過程

react-router中最主要的componentRouter RouterContext Linkhistory庫起到了中間橋樑的做用。

圖片描述

3.2 API層面描述實現過程

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

圖片描述

4. 結語

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

  • 風格: 與React融爲一體,專爲react量身打造,編碼風格與react保持一致,例如路由的配置能夠經過component來實現

  • 簡單: 不須要手工維護路由state,使代碼變得簡單

  • 強大: 強大的路由管理機制,體如今以下方面

    • 路由配置: 能夠經過組件、配置對象來進行路由的配置

    • 路由切換: 能夠經過<Link> Redirect進行路由的切換

    • 路由加載: 能夠同步記載,也能夠異步加載,這樣就能夠實現按需加載

  • 使用方式: 不只能夠在瀏覽器端的使用,並且能夠在服務器端的使用

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

相關文章
相關標籤/搜索