React Router v4入門學(xué)習(xí)記錄: 單頁(yè)應(yīng)用的路由實(shí)踐

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)

codesandbox

三猜谚、安裝

React Router V4被分成了三個(gè)包: react-routerreact-router-domreact-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)境中,有兩種路由組件搞坝,BrowserRouterHashRouter組件搔谴。在下面的單頁(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/6params數(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;

七娇昙、參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尺迂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子冒掌,更是在濱河造成了極大的恐慌噪裕,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件股毫,死亡現(xiàn)場(chǎng)離奇詭異膳音,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)皇拣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門严蓖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人氧急,你說(shuō)我怎么就攤上這事颗胡。” “怎么了吩坝?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵毒姨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我钉寝,道長(zhǎng)弧呐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任嵌纲,我火速辦了婚禮俘枫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逮走。我一直安慰自己鸠蚪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布师溅。 她就那樣靜靜地躺著茅信,像睡著了一般。 火紅的嫁衣襯著肌膚如雪墓臭。 梳的紋絲不亂的頭發(fā)上蘸鲸,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音窿锉,去河邊找鬼酌摇。 笑死膝舅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妙痹。 我是一名探鬼主播铸史,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼怯伊!你這毒婦竟也來(lái)了琳轿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤耿芹,失蹤者是張志新(化名)和其女友劉穎崭篡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吧秕,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琉闪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了砸彬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砂碉,死狀恐怖蛀蜜,靈堂內(nèi)的尸體忽然破棺而出滴某,到底是詐尸還是另有隱情,我是刑警寧澤碍彭,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布女责,位于F島的核電站立砸,受9級(jí)特大地震影響螺戳,放射性物質(zhì)發(fā)生泄漏倔幼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一爽待、第九天 我趴在偏房一處隱蔽的房頂上張望损同。 院中可真熱鬧,春花似錦鸟款、人聲如沸膏燃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蹄梢。三九已至,卻和暖如春富俄,著一層夾襖步出監(jiān)牢的瞬間禁炒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工霍比, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幕袱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓悠瞬,卻偏偏與公主長(zhǎng)得像们豌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浅妆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348