App下載

如何實(shí)現(xiàn)react-router?實(shí)現(xiàn)原理分享!

猿友 2021-07-06 16:48:57 瀏覽數(shù) (3230)
反饋

對(duì)于 react 框架大家并不陌生,由于互聯(lián)網(wǎng)發(fā)展的趨勢(shì) react 的生態(tài)變得越來越豐富了,就拿?flux redux react-router?來說,現(xiàn)在使用的也比較多,那么我們就來聊聊“如何實(shí)現(xiàn) react-router?”這個(gè)問題,下面是有關(guān)于實(shí)現(xiàn)原理的分享,希望對(duì)大家有所幫助。


一、react-router依賴基礎(chǔ)-history

1、History整體介紹

對(duì)于history 來說,它是獨(dú)立的第三方 js 庫,是可以用來兼容在不同環(huán)境和瀏覽器下歷史記錄的管理,而且還擁有統(tǒng)一的 API,在 history中主要分為這幾類:

  • 老瀏覽器的 history : 主要通過hash來實(shí)現(xiàn),對(duì)應(yīng)createHashHistory

  • 高版本瀏覽器: 通過 html5 里面的 history,對(duì)應(yīng)createBrowserHistory。

  • node 環(huán)境下: 主要存儲(chǔ)在 memeory 里面,對(duì)應(yīng)createMemoryHistory

對(duì)于這三個(gè)類,在不同的環(huán)境中還提供了三個(gè)PAI,而且他們有一些相同性質(zhì)的操作,就是將公共文件 createHistory 抽象化,代碼如下所示:

// 內(nèi)部的抽象實(shí)現(xiàn)
function createHistory(options={}) {
  ...
  return {
    listenBefore, // 內(nèi)部的hook機(jī)制,可以在location發(fā)生變化前執(zhí)行某些行為,AOP的實(shí)現(xiàn)
    listen, // location發(fā)生改變時(shí)觸發(fā)回調(diào)
    transitionTo, // 執(zhí)行l(wèi)ocation的改變
    push, // 改變location
    replace,
    go,
    goBack,
    goForward,
    createKey, // 創(chuàng)建location的key,用于唯一標(biāo)示該location,是隨機(jī)生成的
    createPath,
    createHref,
    createLocation, // 創(chuàng)建location
  }
}

對(duì)于上面我們有涉及到的 history 內(nèi)部最基礎(chǔ)的方法分別是:?createHashHistory?、?createBrowserHistory?、?createMemoryHistory?對(duì)于這三個(gè)方法他們只是覆蓋了其中的一些方法,值得注意的是,在這個(gè)時(shí)候的?location?跟瀏覽器原生的?location?是不相同的,然而這個(gè)最大的區(qū)別就在于里面多了?key?字段,?history?內(nèi)部通過?key?來進(jìn)行?location?的操作;代碼如下所示:

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

    

2、 內(nèi)部解析

對(duì)于我們提及到的三個(gè) API 的實(shí)現(xiàn)方法如下:

  • createBrowserHistory: 利用 HTML5 里面的 history。

  • createHashHistory: 通過 hash 來存儲(chǔ)在不同狀態(tài)下的 history 信息。

  • createMemoryHistory: 在內(nèi)存中進(jìn)行歷史記錄的存儲(chǔ)。

3、 執(zhí)行URL前進(jìn)

這三個(gè)方法在執(zhí)行 URL 的方法如下所示:

  • createBrowserHistory: pushState、replaceState。

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

  • createMemoryHistory: 在內(nèi)存中進(jìn)行歷史記錄的存儲(chǔ)。

對(duì)于偽代碼的實(shí)現(xiàn)如下所示:

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

4、 檢測(cè)URL回退

三個(gè)方法的使用方式如下所示:

  • createBrowserHistory: popstate

  • createHashHistory: hashchange。

  • createMemoryHistory: 因?yàn)槭窃趦?nèi)存中操作,跟瀏覽器沒有關(guān)系,不涉及 UI 層面的事情,所以可以直接進(jìn)行歷史信息的回退。

偽代碼的實(shí)現(xiàn)方式如下所示:

// createBrowserHistory(HTML5)中的后退檢測(cè)
function startPopStateListener({ transitionTo }) {
  function popStateListener(event) {
    ...
    transitionTo( getCurrentLocation(event.state) );
  }
  addEventListener(window, 'popstate', popStateListener);
  ...
}

// createHashHistory的后退檢測(cè)
function startPopStateListener({ transitionTo }) {
  function hashChangeListener(event) {
    ...
    transitionTo( getCurrentLocation(event.state) );
  }
  addEventListener(window, 'hashchange', hashChangeListener);
  ...
}
// createMemoryHistory的內(nèi)部實(shí)現(xiàn)
function go(n) {
  if (n) {
    ...
    current += n;
  const currentLocation = getCurrentLocation();
  // change action to POP
  history.transitionTo({ ...currentLocation, action: POP });
  }
}

5、  state的存儲(chǔ)

對(duì)于 state 的存儲(chǔ)來說,我們?cè)跒榱司S護(hù)它的狀態(tài)的時(shí)候,我們會(huì)將其存儲(chǔ)在我們的 sessionStorage 中,代碼如下所示:

// createBrowserHistory/createHashHistory中state的存儲(chǔ)
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僅僅在內(nèi)存中,所以操作比較簡單
const storage = createStateStorage(entries); // storage = {entry.key: entry.state}

function saveState(key, state) {
  storage[key] = state
}
function readState(key) {
  return storage[key]
}

二、react-router的基本原理


 我們先來看一張 react-router 原理圖,如下所示:

react-router原理圖

 


在這張流程中我們可以知道,在 react-router 中,URL對(duì)應(yīng)Location對(duì)象,而UI是由 react components來決定的,這樣就轉(zhuǎn)變成locationcomponents之間的同步問題。


三、react-router具體實(shí)現(xiàn)    

通過上面的知識(shí)我們知道,react-router 在 history 這個(gè)庫類的基礎(chǔ)上實(shí)現(xiàn)URL和UI同步的話分為這另個(gè)層次來描述實(shí)現(xiàn)的具體步驟:

1、  組件層面描述實(shí)現(xiàn)過程 

我們先來看看下面這張流程圖:

react-router的上層實(shí)現(xiàn)流程圖

在這個(gè)流程圖中我們知道最主要的 component 是Router RouterContext Link,對(duì)于history庫的話只有起到了中間橋梁的作用。

2、API層面描述實(shí)現(xiàn)過程 

我們?cè)?API 這個(gè)層次中我們可以通過下面這張流程圖了解,如下所示:

API層面描述實(shí)現(xiàn)過程流程圖


小結(jié):

對(duì)于react-router來說目前是比較受歡迎的,而且也在很多的項(xiàng)目中被大量的使用,它的有點(diǎn)可以分為下面幾點(diǎn):

  • 風(fēng)格: 與React融為一體,專為 react 量身打造,編碼風(fēng)格與 react 保持一致,例如路由的配置可以通過 component 來實(shí)現(xiàn)。

  • 簡單: 不需要手工維護(hù)路由 state,使代碼變得簡單。

  • 強(qiáng)大: 強(qiáng)大的路由管理機(jī)制,體現(xiàn)在如下方面。

    • 路由配置: 可以通過組件、配置對(duì)象來進(jìn)行路由的配置。

    • 路由切換: 可以通過<Link> Redirect進(jìn)行路由的切換。

    • 路由加載: 可以同步記載,也可以異步加載,這樣就可以實(shí)現(xiàn)按需加載。

  • 使用方式: 不僅可以在瀏覽器端的使用,而且可以在服務(wù)器端的使用。

當(dāng)然 react-router 并不是說都沒有缺點(diǎn),它的缺點(diǎn)就是 API 比較不太穩(wěn)定。


總結(jié):

對(duì)于“如何實(shí)現(xiàn)react-router?”這問題和相關(guān)的實(shí)現(xiàn)原理我們通過描述和流程圖有了大致的了解,我們可以通過相對(duì)應(yīng)的學(xué)習(xí)和教程練習(xí)來進(jìn)行鞏固,在這邊小編為大家提供了一個(gè)好用可靠的平臺(tái) W3Cschool,大家可以在平臺(tái)中進(jìn)行更深度的學(xué)習(xí)和了解。當(dāng)然如果你有其他更好的方法也可以和大家一同分享。


0 人點(diǎn)贊