萬惡的根源
距離React Router v4 正式釋出也已經過去三個月了,這周把一個React的架子做了升級,之前的路由用的還是v2.7.0版的,所以決定把路由也升級下,正好“嚐嚐鮮”…
江湖傳言,目前官方同時維護 2.x 和 4.x 兩個版本。(ヾ(。ꏿ﹏ꏿ)ノ゙咦,此刻相信機智如我的你也會發現,ReactRouter v3 去哪兒了?整丟了??巴拉出鍋了???敢不敢給我個完美的解釋!?)事實上 3.x 版本相比於 2.x 並沒有引入任何新的特性,只是將 2.x 版本中部分廢棄 API 的 warning 移除掉而已。按照規劃,沒有歷史包袱的新專案想要使用穩定版的 ReactRouter 時,應該使用 ReactRouter 3.x。目前 3.x 版本也還處於 beta 階段,不過會先於 4.x 版本正式釋出。如果你已經在使用 2.x 的版本,那麼升級 3.x 將不會有任何額外的程式碼變動。
禮貌性簡介下
React Router V4 相較於前面三個版本有根本性變化,首先是遵循Just Component的 API 設計理念,其次API方面也精簡了不少,對新手來說降低了學習難度,但如果是對之前專案的重構,嗯,簡直無**可說。本次升級的主要特點如下:
- 宣告式(Declarative)
- 可組合 (Composability)
React Router V4 遵循了 React 的理念:萬物皆元件。因此 升級之後的 Route、Link、Switch等都是一個普通的元件。
React Router V4 基於 Lerna 管理多個 Repository。在此程式碼庫包括:
- react-router React Router 核心
- react-router-dom 用於 DOM 繫結的 React Router
- react-router-native 用於 React Native 的 React Router
- react-router-redux React Router 和 Redux 的整合
- react-router-config 靜態路由配置幫助助手
外掛初引入
通常我們在 React 的使用中,一般要引入兩個包,react
和 react-dom
,那麼react-router
和react-router-dom
是不是兩個都要引用呢?注意,前方高能,入門第一坑就在這裡。他們兩個只要引用一個就行了,不同之處就是後者比前者多出了<Link> <BrowserRouter>
這樣的 DOM 類元件。因此我們只需引用react-router-dom
這個包就OK了。當然,如果搭配redux
,你還需要使用react-router-redux
。
主要元件簡介
在4.0之前版本的 API 中,<Router>
元件的 children 只能是 React Router 提供的各種元件,如<Route>、<IndexRoute>、<Redirect>
等。而在 React Router 4 中,你可以將各種元件及標籤放進 <Router>
元件中,他的角色也更像是 Redux 中的 <Provider>
。**不同的是<Provider>
是用來保持與 store 的更新,而<Router>
是用來保持與 location 的同步。**示例如下:
// 示例1
<Router>
<div>
<ul>
<li><Link to="/">首頁</Link></li>
<li><Link to="/about">關於</Link></li>
<li><Link to="/topics">主題列表</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</div>
</Router>
複製程式碼
Router是所有路由元件共用的底層介面,一般我們的應用並不會使用這個介面,而是使用高階的路由:
<BrowserRouter>
:使用 HTML5 提供的 history API 來保持 UI 和 URL 的同步;<HashRouter>
:使用 URL 的 hash (例如:window.location.hash) 來保持 UI 和 URL 的同步;<MemoryRouter>
:能在記憶體儲存你 “URL” 的歷史紀錄(並沒有對位址列讀寫);<NativeRouter>
:為使用React Native提供路由支援;<StaticRouter>
:從不會改變地址;
TIPS:算是第二坑吧,和之前的Router不一樣,這裡<Router>
元件下只允許存在一個子元素,如存在多個則會報錯。
反面典型在這裡:
// 示例2
<Router>
<ul>
<li><Link to="/">首頁</Link></li>
<li><Link to="/about">關於</Link></li>
<li><Link to="/topics">主題列表</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/topics" component={Topics}/>
</Router>
複製程式碼
沒錯,示例2在沒有<div>
爸爸的保護下,會報如下異常資訊:
我們知道,Route元件主要的作用就是當一個location匹配路由的path時,渲染某些UI。示例如下:
<Router>
<div>
<Route exact path="/" component={Home}/>
<Route path="/news" component={NewsFeed}/>
</div>
</Router>
// 如果應用的地址是/,那麼相應的UI會類似這個樣子:
<div>
<Home/>
</div>
// 如果應用的地址是/news,那麼相應的UI就會成為這個樣子:
<div>
<NewsFeed/>
</div>
複製程式碼
<Route>
元件有如下屬性:
- path(string): 路由匹配路徑。(沒有path屬性的Route 總是會 匹配);
- exact(bool):為true時,則要求路徑與location.pathname必須完全匹配;
- strict(bool):true的時候,有結尾斜線的路徑只能匹配有斜線的location.pathname;
再次奉上兩個鮮活的例子:
exact配置:
路徑 | location.pathname | exact | 是否匹配 |
---|---|---|---|
/one | /one/two | true | 否 |
/one | /one/two | false | 是 |
strict配置:
路徑 | location.pathname | strict | 是否匹配 |
---|---|---|---|
/one/ | /one | true | 否 |
/one/ | /one/ | true | 是 |
/one/ | /one/two | true | 是 |
同時,新版的路由為<Route>
提供了三種渲染內容的方法:
<Route component>
:在地址匹配的時候React的元件才會被渲染,route props也會隨著一起被渲染;<Route render>
:這種方式對於內聯渲染和包裝元件卻不引起意料之外的重新掛載特別方便;<Route children>
:與render屬性的工作方式基本一樣,除了它是不管地址匹配與否都會被呼叫;
第一種方式沒啥可說的,和之前一樣,這裡我們重點看下<Route render>
的渲染方式:
// 行內渲染示例
<Route path="/home" render={() => <div>Home</div>}/>
// 包裝/合成
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<FadeIn>
<Component {...props}/>
</FadeIn>
)}/>
)
<FadingRoute path="/cool" component={Something}/>
複製程式碼
TIPS: 第三坑! <Route component>
的優先順序要比<Route render>
高,所以不要在同一個<Route>
中同時使用這兩個屬性。
和之前版本沒太大區別,重點看下元件屬性:
- to(string/object):要跳轉的路徑或地址;
- replace(bool):為 true 時,點選連結後將使用新地址替換掉訪問歷史記錄裡面的原地址;為 false 時,點選連結後將在原有訪問歷史記錄的基礎上新增一個新的紀錄。預設為 false;
示例如下:
// Link元件示例
// to為string
<Link to="/about">關於</Link>
// to為obj
<Link to={{
pathname: `/courses`,
search: `?sort=name`,
hash: `#the-hash`,
state: { fromDashboard: true }
}}/>
// replace
<Link to="/courses" replace />
複製程式碼
<NavLink>
是<Link>
的一個特定版本, 會在匹配上當前 URL 的時候會給已經渲染的元素新增樣式引數,元件屬性:
- activeClassName(string):設定選中樣式,預設值為 active;
- activeStyle(object):當元素被選中時, 為此元素新增樣式;
- exact(bool):為 true 時, 只有當地址完全匹配 class 和 style 才會應用;
- strict(bool):為 true 時,在確定位置是否與當前 URL 匹配時,將考慮位置 pathname 後的斜線;
- isActive(func):判斷連結是否啟用的額外邏輯的功能;
從這裡我們也可以看出,新版本的路由在元件化上面確實下了不少功夫,來看看NavLink的使用示例:
// activeClassName選中時樣式為selected
<NavLink
to="/faq"
activeClassName="selected"
>FAQs</NavLink>
// 選中時樣式為activeStyle的樣式設定
<NavLink
to="/faq"
activeStyle={{
fontWeight: `bold`,
color: `red`
}}
>FAQs</NavLink>
// 當event id為奇數的時候,啟用連結
const oddEvent = (match, location) => {
if (!match) {
return false
}
const eventID = parseInt(match.params.eventID)
return !isNaN(eventID) && eventID % 2 === 1
}
<NavLink
to="/events/123"
isActive={oddEvent}
>Event 123</NavLink>
複製程式碼
該元件用來渲染匹配地址的第一個<Route>
或者<Redirect>
。那麼它與使用一堆route又有什麼區別呢?
<Switch>
的獨特之處是獨它僅僅渲染一個路由。相反地,每一個包含匹配地址(location)的<Route>
都會被渲染。思考下面的程式碼:
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
複製程式碼
如果現在的URL是/about
,那麼<About>
, <User>
, 還有<NoMatch>
都會被渲染,因為它們都與路徑(path)匹配。這種設計,允許我們以多種方式將多個<Route>
組合到我們的應用程式中,例如側欄(sidebars),麵包屑(breadcrumbs),bootstrap tabs等等。 然而,偶爾我們只想選擇一個<Route>
來渲染。如果我們現在處於/about
,我們也不希望匹配/:user
(或者顯示我們的 “404” 頁面 )。以下是使用 Switch 的方法來實現:
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
</Switch>
複製程式碼
現在,如果我們處於/about
,<Switch>
將開始尋找匹配的<Route>
。<Route path="/about"/>
將被匹配, <Switch>
將停止尋找匹配並渲染<About>
。同樣,如果我們處於/michael
,<User>
將被渲染。
以上就是我對React Router v4 的初試,反正也是一邊查文件,一邊試水的,如有錯誤或疏漏,還請大家諒解並不吝指正!