React Router V4(俗稱 第四代react-router
)相對(duì)V2/V3幾乎完全重寫了帜讲,遵循 Just Component 的 API 設(shè)計(jì)理念。
本篇文章中癞己,我們會(huì)構(gòu)建一個(gè)簡(jiǎn)單的單頁(yè)應(yīng)用膀斋,來(lái)理解React Router的基本使用,內(nèi)容:
- 選擇哪種路由: HashRouter痹雅、BrowserRouter
- Route的核心內(nèi)容: path仰担、exact、component绩社、render等
- 使用Link實(shí)現(xiàn)路由跳轉(zhuǎn)
一摔蓝、如何構(gòu)建單頁(yè)應(yīng)用?
假如你還不會(huì)構(gòu)建一個(gè)簡(jiǎn)單的單頁(yè)應(yīng)用愉耙,請(qǐng)參考筆者之前的React入門文章 - react入門教程01 - 簡(jiǎn)介贮尉、開發(fā)環(huán)境搭建、創(chuàng)建第一個(gè)React應(yīng)用
二朴沿、完整代碼實(shí)現(xiàn)
三猜谚、安裝
React Router V4被分成了三個(gè)包: react-router
、react-router-dom
和 react-router-native
赌渣。
其中第一個(gè)react-router
是不能被直接安裝的龄毡。該包提供了路由的核心組件和方法。另外兩個(gè)包分別提供了兩個(gè)環(huán)境的路由: 瀏覽器
和原生
锡垄。
這里,我們構(gòu)建單頁(yè)網(wǎng)頁(yè)祭隔,只需要安裝react-router-dom
货岭。
// npm
npm install --save react-router-dom
// yarn
yarn add react-router-dom
四、核心概念
4.1 Router
開始具體的代碼實(shí)現(xiàn)之前疾渴,我們需要決定使用哪一類型路由千贯。在瀏覽器運(yùn)行環(huán)境中,有兩種路由組件搞坝,BrowserRouter
和HashRouter
組件搔谴。在下面的單頁(yè)應(yīng)用中,我們使用BrowserRouter
桩撮。
4.2 History
每個(gè)路由對(duì)象會(huì)創(chuàng)建一個(gè)歷史history
對(duì)象敦第,<BrowserRouter>
創(chuàng)建 browser history
, <HashRouter>
創(chuàng)建 hash history
。若你想深入理解history
店量,參考這篇文章芜果。
4.3 Routes
在V3中,<Route>
不是一個(gè)真正的組件融师,而是作為一個(gè)標(biāo)簽用來(lái)創(chuàng)建route配置對(duì)象右钾。
使用 v4,您可以像常規(guī)的 React 程序一樣布局您應(yīng)用中的組件,您要根據(jù)位置(特別是其 pathname )呈現(xiàn)內(nèi)容的任何位置舀射,您將呈現(xiàn) <Route>
窘茁。
在 v4,<Route>
其實(shí)是一個(gè)組件脆烟,所以無(wú)論你在哪里渲染 <Route>
山林,它里面的內(nèi)容都會(huì)被渲染。當(dāng) <Route>
的 path 與當(dāng)前的路徑匹配時(shí)浩淘,它將會(huì)渲染 component
, render
, orchildren
屬性中的內(nèi)容捌朴,當(dāng) <Route>
的 path 與當(dāng)前的路徑不匹配時(shí),將會(huì)渲染 null
张抄。
4.4 Path
Router
需要一個(gè)path
字符串屬性砂蔽,描述路由的路徑地址。比如署惯,<Route path='/roster' />
會(huì)匹配以/roster
為前綴的路徑地址左驾。當(dāng)匹配到當(dāng)前的路徑時(shí),路由會(huì)渲染一個(gè)React元素极谊;不匹配的話诡右,就不會(huì)渲染。
<Route path='/roster'/>
// when the pathname is '/', the path does not match
// when the pathname is '/roster' or '/roster/2', the path matches
// If you only want to match '/roster', then you need to use
// the "exact" prop. The following will match '/roster', but not
// '/roster/2'.
<Route exact path='/roster'/>
// You might find yourself adding the exact prop to most routes.
// In the future (i.e. v5), the exact prop will likely be true by
// default. For more information on that, you can check out this
// GitHub issue:
// https://github.com/ReactTraining/react-router/issues/4958
4.5 match
當(dāng)路由的path匹配時(shí)轻猖,會(huì)創(chuàng)建一個(gè)match
對(duì)象帆吻,該對(duì)象的屬性如下:
-
url
- 當(dāng)前的路徑 -
path
- path屬性值 -
isExact
- path == pathname -
params
- 通過(guò)path-to-regexp
從路徑中抓取的數(shù)據(jù)
你可以通過(guò)[route tester](https://pshrmn.github.io/route-tester/#/)
網(wǎng)站來(lái)玩轉(zhuǎn)路由match實(shí)現(xiàn)
更多路徑匹配,請(qǐng)看這里咙边。?
4.6 Switch
在v3中猜煮,您可以指定一些子路由,并且只會(huì)渲染匹配到的第一個(gè)败许。
v4 通過(guò) <Switch>
組件提供了相似的功能王带,當(dāng) <Switch>
被渲染時(shí),它僅會(huì)渲染與當(dāng)前路徑匹配的第一個(gè)子 <Route>
市殷。
4.6 Redirect
如果你想從一個(gè)路徑重定向到另一個(gè)愕撰,在V4,你可以使用<Redirect>達(dá)到效果醋寝。
// v4
<Route exact path="/" render={() => <Redirect to="/welcome" component={App} />} />
<Switch >
<Route exact path="/" component={App} />
<Route path="/login" component={Login} />
<Redirect path="*" to="/" />
</Switch>
4.7 Link
跳轉(zhuǎn)用的標(biāo)簽搞挣。
五、編碼部分
接下來(lái)音羞,開始實(shí)現(xiàn)編碼部分柿究。
5.1 <App>
首先,我們先定義一個(gè) <App>
組件黄选。為簡(jiǎn)化實(shí)現(xiàn)蝇摸,我們將應(yīng)用分成兩部分: <Header>
婶肩、<Main>
利凑。Heander組件包含應(yīng)用的導(dǎo)航鏈接捺信;Main組件負(fù)責(zé)內(nèi)容的渲染瘸味。
import React from 'react'
import Header from './Header'
import Main from './Main'
const App = () => (
<div>
<Header />
<Main />
</div>
);
export default App
5.2 創(chuàng)建路由
下面登夫,我們定義該單頁(yè)應(yīng)用的路徑地址:
-
/
- 首頁(yè) -
/roster
- 團(tuán)隊(duì)執(zhí)勤名單 -
/roster/:number
- 團(tuán)隊(duì)成員的個(gè)人頁(yè) -
/schedule
- 執(zhí)勤安排表
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/roster' component={Roster} />
<Route exact path='/schedule' component={Schedule} />
</Switch>
Route
有三個(gè)屬性可以用來(lái)渲染組件围苫,每個(gè)<Route>
只能有其中一個(gè)屬性值磕潮。
-
component
- 它的值是一個(gè)組件效床,在path匹配成功之后會(huì)繪制這個(gè)組件候引; -
render
- 一個(gè)返回React組件的方法们童,跟component
的效果差不多畔况,但可以傳入“render prop”; -
children
- 返回一個(gè)React組件的方法慧库,這個(gè)總是會(huì)繪制跷跪,即使沒(méi)有匹配的路徑的時(shí)候。
<Route path='/page' component={Page} />
const extraProps = { color: 'red' }
<Route path='/page' render={(props) => (
<Page {...props} data={extraProps}/>
)}/>
<Route path='/page' children={(props) => (
props.match
? <Page {...props}/>
: <EmptyPage {...props}/>
)}/>
通常齐板,component屬性和render屬性會(huì)經(jīng)常被使用吵瞻,而children屬性不太常用。我們暫時(shí)不需要傳入額外的參數(shù)給渲染組件甘磨,所以這里的<Route>
使用component屬性橡羞。
上述定義了該應(yīng)用的基礎(chǔ)路由結(jié)構(gòu),接下來(lái) 給出完成的Main組件實(shí)現(xiàn):
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import Home from './Home'
import Roster from './Roster'
import Schedule from './Schedule'
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/roster' component={Roster} />
<Route exact path='/schedule' component={Schedule} />
</Switch>
</main>
)
export default Main
5.3 嵌套路由
團(tuán)隊(duì)成員的個(gè)人頁(yè)/roster/:number
并未在上述的實(shí)現(xiàn)中定義济舆。實(shí)際上卿泽,這個(gè)路由會(huì)被這個(gè)以/roster
為前綴的<Roster>
組件渲染。
在Roster
組件中滋觉,我們需要渲染兩部分內(nèi)容:
-
/roster
- 完整的團(tuán)隊(duì)執(zhí)勤名單頁(yè)签夭,使用exact
完全匹?配 -
/roster/:number
- 團(tuán)隊(duì)成員的個(gè)人頁(yè),路由通過(guò)抓取/roster
后面的地址名字來(lái)獲取參數(shù)
const Roaster = () => (
<Switch>
<Route exact path='/roster' component={FullRoster} />
<Route path='/roster/:number' component={Player} />
</Switch>
)
這種方式可以很好地將相同前綴的一組路由 統(tǒng)一放入一個(gè)組件中實(shí)現(xiàn)椎瘟,更好的管理、歸類侄旬。
下面是完整的<Roster>
實(shí)現(xiàn):
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import FullRoster from './FullRoster'
import Player from './Player'
const Roster = () => (
<div>
<h2>This is a roster page!</h2>
<Switch>
<Route exact path='/roster' component={FullRoster} />
<Route path='/roster/:number' component={Player} />
</Switch>
</div>
)
export default Roster
5.4 路由參數(shù)
有時(shí)候我們需要抓取路徑地址中的參數(shù)肺蔚,例如,團(tuán)隊(duì)成員的個(gè)人頁(yè)需要抓取員工的工號(hào)儡羔。
/roster/:number
中的:number
就是需要抓取的員工工號(hào)宣羊,被儲(chǔ)存在match.params.number
。例如汰蜘,/roster/6
的params
數(shù)據(jù)是: { number: '6' } // note that the captured value is a string
仇冯。
<Player>
組件可以使用props.match.params
數(shù)據(jù)來(lái)渲染相應(yīng)員工的數(shù)據(jù)。
const Player = props => {
const player = PlayerApi.get(
parseInt(props.match.params.number, 10)
);
if (!player) {
return (<div>Sorry, but the player was not found.</div>)
}
return (
<div>
<h1>{player.name} (#{player.number})</h1>
<h2>Position: {player.position}</h2>
<Link to='/roster'>Back</Link>
</div>
);
};
5.5 <FullRoster>族操、<Schedule>
其他組件的實(shí)現(xiàn):
// Schedule.js
import React from "react";
const Schedule = () => (
<div>
<ul>
<li>6/5 @ Evergreens</li>
<li>6/8 vs Kickers</li>
<li>6/14 @ United</li>
</ul>
</div>
);
export default Schedule;
// FullRoster.js
import React from "react";
import { Link } from "react-router-dom";
import PlayerAPI from "../api";
const FullRoaster = () => (
<div>
<ul>
{PlayerAPI.all().map(p => (
<li key={p.number}>
<Link to={`/roster/${p.number}`}>{p.name}</Link>
</li>
))}
</ul>
</div>
);
export default FullRoaster;
六苛坚、最后
我們最后實(shí)現(xiàn)<Header>
組件使用<Link>
元素將所有頁(yè)面串聯(lián)起來(lái)比被,當(dāng)你點(diǎn)擊<Link>
時(shí),URL更新的同時(shí)泼舱,僅重新渲染相應(yīng)的組件等缀,并不需要全部重新渲染。
import React from "react";
import { Link } from "react-router-dom";
// The Header creates links that can be used to navigate
// between routes.
const Header = () => (
<header>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/roster">Roster</Link>
</li>
<li>
<Link to="/schedule">Schedule</Link>
</li>
</ul>
</nav>
</header>
);
export default Header;