React Router V4(俗称 第四代react-router
)相对V2/V3几乎完全重写了,遵循 Just Component 的 API 设计理念。
本篇文章中,我们会构建一个简单的单页应用,来理解React Router的基本使用,内容:
- 选择哪种路由: HashRouter、BrowserRouter
- Route的核心内容: path、exact、component、render等
- 使用Link实现路由跳转
一、如何构建单页应用?
假如你还不会构建一个简单的单页应用,请参考笔者之前的React入门文章 - react入门教程01 - 简介、开发环境搭建、创建第一个React应用
二、完整代码实现
三、安装
React Router V4被分成了三个包: react-router
、react-router-dom
和 react-router-native
。
其中第一个react-router
是不能被直接安装的。该包提供了路由的核心组件和方法。另外两个包分别提供了两个环境的路由: 浏览器
和原生
。
这里,我们构建单页网页,只需要安装react-router-dom
。
// npm
npm install --save react-router-dom
// yarn
yarn add react-router-dom
四、核心概念
4.1 Router
开始具体的代码实现之前,我们需要决定使用哪一类型路由。在浏览器运行环境中,有两种路由组件,BrowserRouter
和HashRouter
组件。在下面的单页应用中,我们使用BrowserRouter
。
4.2 History
每个路由对象会创建一个历史history
对象,<BrowserRouter>
创建 browser history
, <HashRouter>
创建 hash history
。若你想深入理解history
,参考这篇文章。
4.3 Routes
在V3中,<Route>
不是一个真正的组件,而是作为一个标签用来创建route配置对象。
使用 v4,您可以像常规的 React 程序一样布局您应用中的组件,您要根据位置(特别是其 pathname )呈现内容的任何位置,您将呈现 <Route>
。
在 v4,<Route>
其实是一个组件,所以无论你在哪里渲染 <Route>
,它里面的内容都会被渲染。当 <Route>
的 path 与当前的路径匹配时,它将会渲染 component
, render
, orchildren
属性中的内容,当 <Route>
的 path 与当前的路径不匹配时,将会渲染 null
。
4.4 Path
Router
需要一个path
字符串属性,描述路由的路径地址。比如,<Route path='/roster' />
会匹配以/roster
为前缀的路径地址。当匹配到当前的路径时,路由会渲染一个React元素;不匹配的话,就不会渲染。
<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
当路由的path匹配时,会创建一个match
对象,该对象的属性如下:
-
url
- 当前的路径 -
path
- path属性值 -
isExact
- path == pathname -
params
- 通过path-to-regexp
从路径中抓取的数据
你可以通过[route tester](https://pshrmn.github.io/route-tester/#/)
网站来玩转路由match实现
更多路径匹配,请看这里。�
4.6 Switch
在v3中,您可以指定一些子路由,并且只会渲染匹配到的第一个。
v4 通过 <Switch>
组件提供了相似的功能,当 <Switch>
被渲染时,它仅会渲染与当前路径匹配的第一个子 <Route>
。
4.6 Redirect
如果你想从一个路径重定向到另一个,在V4,你可以使用<Redirect>达到效果。
// 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
跳转用的标签。
五、编码部分
接下来,开始实现编码部分。
5.1 <App>
首先,我们先定义一个 <App>
组件。为简化实现,我们将应用分成两部分: <Header>
、<Main>
。Heander组件包含应用的导航链接;Main组件负责内容的渲染。
import React from 'react'
import Header from './Header'
import Main from './Main'
const App = () => (
<div>
<Header />
<Main />
</div>
);
export default App
5.2 创建路由
下面,我们定义该单页应用的路径地址:
-
/
- 首页 -
/roster
- 团队执勤名单 -
/roster/:number
- 团队成员的个人页 -
/schedule
- 执勤安排表
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/roster' component={Roster} />
<Route exact path='/schedule' component={Schedule} />
</Switch>
Route
有三个属性可以用来渲染组件,每个<Route>
只能有其中一个属性值。
-
component
- 它的值是一个组件,在path匹配成功之后会绘制这个组件; -
render
- 一个返回React组件的方法,跟component
的效果差不多,但可以传入“render prop”; -
children
- 返回一个React组件的方法,这个总是会绘制,即使没有匹配的路径的时候。
<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属性会经常被使用,而children属性不太常用。我们暂时不需要传入额外的参数给渲染组件,所以这里的<Route>
使用component属性。
上述定义了该应用的基础路由结构,接下来 给出完成的Main组件实现:
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 嵌套路由
团队成员的个人页/roster/:number
并未在上述的实现中定义。实际上,这个路由会被这个以/roster
为前缀的<Roster>
组件渲染。
在Roster
组件中,我们需要渲染两部分内容:
-
/roster
- 完整的团队执勤名单页,使用exact
完全匹�配 -
/roster/:number
- 团队成员的个人页,路由通过抓取/roster
后面的地址名字来获取参数
const Roaster = () => (
<Switch>
<Route exact path='/roster' component={FullRoster} />
<Route path='/roster/:number' component={Player} />
</Switch>
)
这种方式可以很好地将相同前缀的一组路由 统一放入一个组件中实现,更好的管理、归类。
下面是完整的<Roster>
实现:
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 路由参数
有时候我们需要抓取路径地址中的参数,例如,团队成员的个人页需要抓取员工的工号。
/roster/:number
中的:number
就是需要抓取的员工工号,被储存在match.params.number
。例如,/roster/6
的params
数据是: { number: '6' } // note that the captured value is a string
。
<Player>
组件可以使用props.match.params
数据来渲染相应员工的数据。
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>
其他组件的实现:
// 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;
六、最后
我们最后实现<Header>
组件使用<Link>
元素将所有页面串联起来,当你点击<Link>
时,URL更新的同时,仅重新渲染相应的组件,并不需要全部重新渲染。
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;
网友评论