react-router 2.7.0源碼深度分析

前言

在前端單頁面應用裏面,路由是比較重要的部分,筆者的上一篇博文簡單的路由介紹了簡單的路由內部機制,本文則將分析react-router的內部機制。前端

介紹

react-router爲react提供路由管理,爲基於jsx格式的app系統提供了方便的切換頁面功能。
它在前端提供給了2種方式,經過hashchange或者瀏覽器原生的history api進行地址更新,上一篇介紹了hash的方式,本文則以history api的形式切入分析。react

代碼剖析

路由配置

react-router本生爲react組建,內部組建如Router,Route,IndexRoute, Redirect,Link等。
如下是摘自react-router example的路由配置git

<Router history={withExampleBasename(browserHistory, __dirname)}>
    <Route path="/" component={App}>
        <IndexRoute component={Index}/>
        <Route path="/about" component={About}/>
        <Route path="users" component={Users}>
            <IndexRoute component={UsersIndex}/>
            <Route path=":id" component={User}/>
        </Route>
    </Route>
</Router>

點此獲取完整代碼github

對應結構圖

在初始化過程當中他會以children形式讀入Router生命週期內,在被轉化爲數組,此時它內部的結構以下
圖片描述segmentfault

路由初始化

初始化browserHistory對象

react-router依賴history^2.0模塊生成的history對象,而後在Router生命週期componentWillMount中加入對應的封裝如basename,query
useQueries.js 對history對象內的方法進行封裝api

function listen(listener) {
  return history.listen(function (location) {
    listener(addQuery(location))
  })
}

// Override all write methods with query-aware versions.
function push(location) {
  history.push(appendQuery(location, location.query))
}

useBasename.js 對history對象內的方法進行封裝數組

function listen(listener) {
      return history.listen(function (location) {
        listener(addBasename(location))
      })
    }

    // Override all write methods with basename-aware versions.
    function push(location) {
      history.push(prependBasename(location))
    }

Router.js 對history對象增長setRouteLeaveHook鉤子函數以及isActive函數瀏覽器

最終生成router對象 以this.router = router存在Router組建內部,this.history 已過期(issues),不建議使用react-router

初始化監聽事件

this._unlisten = transitionManager.listen(function (error, state) {
  if (error) {
    _this.handleError(error);
  } else {
    _this.setState(state, _this.props.onUpdate);
  }
});


function listen(listener) {
changeListeners.push(listener);
if (location) {
  listener(location);
} else {
  var _location = getCurrentLocation();
  allKeys = [_location.key];
  updateLocation(_location);
}

此時總體初始化完畢app

改變路由

<Link to="about" activeStyle={ACTIVE}>/</Link>

以一次Link點擊爲例

  • 觸發Link組建的handleClick方法
  • 調用router對象 push方法
  • 拼裝location對象
  • 改變url欄的地址
  • 調用updateLocation 觸發changeListeners內的全部監聽事件
  • 回調函數內調用match方法,根據location對象正則匹配router對象,匹配出對應的組建 執行runLeaveHooks鉤子
  • 調用Router組建的setState(nextstate)
  • Router-context組建的render方法調用createElement
  • 調用react.createElement
  • 完成渲染

簡潔的流轉圖

放上一個本人總結的一個簡單流轉過程圖
圖片描述

詳細流程圖

對簡潔的流程圖熟悉以後,則可深刻了解內部機制的細節以下圖
圖片描述

概括

雖然源碼繁瑣複雜,可是內部的核心還是圍繞着下面3塊動做作一系列的封裝.

  • 註冊監聽事件:封裝history對象的,生成router對象存儲在Router內,並經過其註冊監聽事件,綁定相應的回調函數
  • 觸發監聽事件:經過Link/browserHistory.push/瀏覽器回退快進/dom ready 等四種方式觸發回調函數
  • 回調函數: Router內的setState(next)最終觸發react.createElement進而更新UI

圖片描述

最後

本文有什麼不完善的地方,或者流程圖有待改進的地方,敬請斧正。

相關文章
相關標籤/搜索