React Router 4是一组导航组件,在你的React应用程序中提供声明性路由。在这个教程中,将通过一个实际的例子来介绍React Router 4是怎样使用的。
在几乎每个应用程序的体系结构中,路由都是极其重要的。应用程序越大,路由功能就越复杂,从简单到深度嵌套的路由场景。
在React构建的应用程序中,React Router 是最受欢迎使用最普遍的路由库,随着你的应用程序不断变大变复杂,那就需要多个视图和路由,选择一个好的路由库来帮助管理视图之间的转换、重定向、获取URL参数等,让这些操作变得更加的简单,方便。
在此之前,之前版本的React Router涉及预先声明应用程序的路由,在呈现之前声明文件中的所有路径作为应用程序初始化的一部分。使用React Router 4,你可以以声明方式进行路由。 React Router 4的API基本上都是组件,因此如果你已经在React中组合了组件,它就很容易使用。让我们开始吧!
设置和安装
你需要:
- Node.js (6.0版本或更高版本) 和 npm.
- create-react-app用于引导新项目。
React Router有这几个包组成:react-router
、react-router-dom
和react-router-native
。
- react-router: 包括核心路由组件。
- react-router-dom:包含浏览器所需的路由API。
- react-router-native:包含移动端应用所需的路由API。
使用create_react_app创建一个新项目,然后导航到如下所示创建的目录。
create-react-app bose
cd bose
安装 react-router-dom
。
npm install --save react-router-dom
我们将覆盖哪些内容?
我们将专注于在浏览器端使用React Router 4。我们将介绍下面列出的非常重要的概念:
- 基本路由
- 嵌套路由和url参数
- 路由保护和认证
- 自定义Link组件
- 处理不存在的路由
- 渲染SideBar
基本路由(Basic Routing)
在React应用中有两种路由组件可用,BrowserRouter
和 HashRouter
,前者组成的url不带#,而后者则带有。
注意:如果要构建支持旧版浏览器的Web应用程序,建议使用HashRouter。
打开src/index.js文件,并添加以下代码:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
, document.getElementById('root'));
registerServiceWorker();
在上面代码中,我们从react-router-dom
导入了BrowserRouter
, Route
, 和 Link
组件,并通过Router组件(是BrowserRouter的别名)包裹App组件。路由器组件是成功路由的第一步,它充当其他每个路由组件的容器。另外,Router
组件只能有唯一一个子元素或子组件。现在,我们该怎样定义路由呢?
打开src/App.js
,我们将路由定义在这里。
import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom';
import './App.css';
const Home = () => (
Home
);
const Airport = () => (
- Jomo Kenyatta
- Tambo
- Murtala Mohammed
);
const City = () => (
- San Francisco
- Istanbul
- Tokyo
);
class App extends Component {
render() {
return (
- Home
- Airports
- Cities
);
}
}
export default App;
在上面代码中,使用了Link
组件将用户定向到/
, /airports
, 和/cities
。这些链接中的每一个都有一个组件,一旦当前位置与路由的路径匹配,就应该渲染该组件。然而,事实并不是这样的。让我们接着往下看。
Airports route
作为Home
组件的视图Home
应仅匹配/
时,才在根组件上显示。然后它在所有的路由上都渲染了。路径/
匹配/airports
和/cities
路由。因此会在其他两个路由中渲染Home
组件,那么比较简单的解决方法是在/
的路由组件上添加exact
。
src/App.js
-
Airports
路由现在就没有渲染Home
UI组件了*。
在上面的例子中,所有的
组件有一个component
属性,作用是当访问的URL匹配所配置的路由的路径时,渲染一个组件。如果你只想渲染一个小函数而不是整个组件,该怎么办?正如下面的代码一样,你可以使用render
属性来展示。
( This is the airport route )}/>
嵌套路由和URL参数
如果你希望URL像/courses/business
,/courses/technology/
,你会怎么去实现它?
src/App.js
import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom';
import './App.css';
const Courses = ({ match }) => (
- Technology
- Business
- Economics
( This is technology )}/>
( This is business )}/>
( This is economics )}/>
);
/* Home Component */ // code hidden
/* City Component */ //code hidden
class App extends Component {
render() {
return (
- Home
- Courses
- Cities
);
}
}
export default App;
如果当前的URL匹配路径/courses
,然后通过Courses
组件渲染了technology、business、economics链接,更进一步来说,如果当前的URL匹配/courses/technology
、/courses/business
和 /courses/economics
等的路径,This is technology
, This is business
与This is economics
就能够渲染呈现在页面上。
作为一名开发人员,我相信你已经用一双重构眼睛看着这种方法了。在上面比较简单的代码中,有很多重复和硬编码。代码行越多,改变理由就越难。让我们来重构一下。
React Router 4附带了一个匹配API,当一个路由的路径成功与当前的URL匹配时创建这个匹配对象。这个匹配对象有一些属性,但是我将列出你应该马上知道的一些属性:
- match.url: 返回一个显示URL的字符串
- match.path:返回一个显示路由的路径字符串
- match.params:返回一个具有从URL解析的值的对象
让我们一步一步的重构,就像这样使用匹配对象来重构Courses
组件:
const Courses = ({ match }) => (
- Technology
- Business
- Economics
( This is technology )}/>
( This is business )}/>
( This is economics )}/>
);
测试一下如果你的URL是正常运行的,使用match.path
来对Route
组件做同样的事。
const Courses = ({ match }) => (
- Technology
- Business
- Economics
( This is technology )}/>
( This is business )}/>
( This is economics )}/>
);
检查你的应用,应该是处于较好的运行状态的。现在最后一步,我们可以使用一行代码来替换上面的三行代码。
const Courses = ({ match }) => (
- Technology
- Business
- Economics
( This is {match.params.course} )}/>
);
我们使用了match.params
,它针对URL的位置提供了一个键值对的对象。:course
是URL的参数。因此match.params.course
针对正确的URL提供了一个值。
保护路由和认证
当开发一个web应用时,在某些情况下,必须保护某些路由不被访问。在大多数情况下,这些路由只能被授权用户所访问。
在之前的React Router版本,如v3。保护路由的代码像下面这样:
index.js
const Root = () => {
return (
)
}
组件有一个onEnter
属性,它接受一种允许根据用户的身份验证状态输入或拒绝URL位置的方法。现在,它与React Router 4不同。
让我们来创建三个组件,分别是Public,
Private
, Login
。
App.js
import React, { Component } from 'react';
import {
Route,
Link,
BrowserRouter as Router,
} from 'react-router-dom';
const Public = () => (
This is a public page
);
const Private = () => (
This is a private page
);
const Login = () => (
Login Page
);
class App extends Component {
render() {
return (
- Public
- Private
);
}
}
export default App;
现在我们能够访问/public
, /private
这两个路由。现在,让我们来确保/private
路由不能被访问,知道用户已经登录了。React Router 4使用了一个声明式的方法,所以我们能够方便地使用一个如
的组件,然而react router 4并没有提供它,我们来构建它。我们想到了一个授权服务。
在这个例子中,授权服务(Auth Service)是一个如下简单的对象:
const AuthService = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 100)
},
logout(cb) {
this.isAuthenticated = false
setTimeout(cb, 100)
}
}
现在,我们来构建
:
const SecretRoute = ({ component: Component, ...rest }) => (
(
AuthService.isAuthenticated === true
?
:
)} />
);
上面的代码简单地说明了,当授权状态对于用户是true,组件将渲染,否则用户可能被重定向到/login
路由,让我们来试一下。
App.js
import React, { Component } from 'react';
import {
Route,
Link,
Redirect,
BrowserRouter as Router,
} from 'react-router-dom';
const Login = () => (
Login Page
);
const AuthService = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 100)
},
logout(cb) {
this.isAuthenticated = false
setTimeout(cb, 100)
}
};
const SecretRoute = ({ component: Component, ...rest }) => (
(
AuthService.isAuthenticated === true
?
:
)} />
);
class App extends Component {
render() {
return (
- Public
- Private
);
}
}
export default App;
当链接Private
链接时,被迫重定向到了/login
路由。很好,让我们更进一步通过尝试实际的登录和注销流程。像下面这样修改login组件:
App.js
...
class Login extends React.Component {
state = {
redirectToPreviousRoute: false
};
login = () => {
AuthService.authenticate(() => {
this.setState({ redirectToPreviousRoute: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToPreviousRoute } = this.state;
if (redirectToPreviousRoute) {
return ;
}
return (
You must log in to view the page at {from.pathname}
);
}
}
我们修改了登录组件,增加了一个login方法并且当用户拒绝访问的时候重定向到用户想要登录进入的组件,这对于你的路由系统来说是一种典型的行为,并且当用户访问的时候,会重定向到另一个页面。
现在我们在
中我们不得不修改
组件中的属性:
const SecretRoute = ({ component: Component, ...rest }) => (
(
AuthService.isAuthenticated === true
?
:
)} />
);
到这里差不多完成了。然而,当用户成功登陆获取授权后提供一个退出登录按钮是不是更好呢?让我们创建一个
组件。
App.js
...
const AuthStatus = withRouter(({ history }) => (
AuthService.isAuthenticated ? (
Welcome!
) : (
You are not logged in.
)
));
在上面简单的示例代码中,我们使用了withRouter
和history.push
。其中withRouter
是一个来自React Router 的高阶组件,当拥有相同属性的路由改变的时候它能够重新渲染组件。history.push
是使用React Router中的
组件重定的一种方法。
现在,继续渲染
组件
App.js
class App extends Component {
render() {
return (
- Public
- Private
);
}
}
现在,重新在浏览器中试一下,你应该能够成功地登录和退出登录。
自定义Link组件
怎样自定义Link组件?其实很简单。你将学习如何自定义链接,以便在特定链接处于激活状态时具有独特外观,React Router 4有一种很容易实现这个任务的方法。
你的App.js
代码如下:
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
const Home = () => (
Home Page
)
const Contact = () => (
Contact Page
)
class App extends React.Component {
render() {
return (
Home
Contact
)
}
}
export default App;
负责使管理不同的处于激活状态的链接。
const CustomLink = ({ children, to, exact }) => (
(
{match ? '> ' : ''}
{children}
)}/>
);
这并不复杂,
能够驱动
。在上面的代码中,当路由路径匹配URL位置时,使用了match
对象来决定是否添加>
标志。
这里有三种渲染
组件的方式:
,
与
。上面的代码使用了children
属性,此渲染属性接受一个函数,该函数接收与组件和渲染函数相同的所有路径属性,除非路径与URL位置不匹配。此过程使你能够根据路由是否匹配来动态调整UI。并且也是我们需要的来创建一个自定义Link组件的方式。
处理不存在的路由
作为一个开发者,你需要处理某个路由不存在的场景。如果一个用户访问了你的网站,并且访问了一个不存在的路由,如/babalawo
,你会做什么?难道你就这样准许你的网站挂了?接下来一起来处理这样的场景。
App.js
import React, { Component } from 'react';
import {
Route,
Link,
Redirect,
Switch,
BrowserRouter as Router,
} from 'react-router-dom';
const Home = () => (
Home Page
)
const Contact = () => (
Contact Page
)
class App extends Component {
render() {
return (
-
Home
-
Contact
( Sorry, this page does not exist. )} />
);
}
}
export default App;
在上面的代码中,我们引入了一个来自React Router新的组件
,并将我们的组件包裹在
组件中。现在,如果访问的URL链接已所定义的带有路径的路由不匹配的话,
组件引用了一个没有配置路径,且只有一个render方法的
。
在你的浏览器中试着访问一个不存在的URL,网页上将显示一条Sorry, this page does not exist的信息。
渲染侧边栏
长时间里,侧边栏一直存在于app中,让我们学习使用React Router 4怎样创建一个侧边栏。第一步就是将我们的路由放在一个数组中:
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link,
} from 'react-router-dom'
const routes = [
{ path: '/',
exact: true,
leftbar: () => Home,
main: () => Home
},
{ path: '/about',
leftbar: () => About,
main: () => About
},
{ path: '/contact',
leftbar: () => Contact,
main: () => Contact
}
]
class App extends React.Component {
render() {
return (
- Home
- About
- Contact
)
}
}
export default App
在上面的代码中,我们定义了一个leftbar
和main
的键,他们很快就会派上用场,让我们的工作变得非常轻松。
现在我们要做的就是遍历这个数组:
App.js
render() {
return (
- Home
- About
- Contact
{routes.map((route) => (
))}
{routes.map((route) => (
))}
)
}