一、先简单交代几句
对于之前没有接触过 React Router 的同学来说,一定要注意:最新的React Router版本是 V5 。除此之外,还有 V2 V3 V4 版本。其中V4和V5版本的用法和理念基本上是一致的,但是和之前的V2、V3版本有较大差异。
所以在学习之前一定要弄清楚自己使用的是哪一个版本,如果是V4或者V5,V4和V5两个版本的文档笔记都可以参考;如果你用的是V2或者V3,那么一定要找到对应的文档笔记。
在我学习React Router的时候,使用的是V5,所以这篇笔记也是对 React Router V5 的一些总结。如果你是一名React小白,我想通过这篇笔记,你可以基本会使用React Router。如果你想更深层次地理解React Router,可以访问:reacttraining.com/react-route… 。
二、React Router 的基本使用
1. 安装
在V4和V5版本中,React Router分成了两个大的方向:React Web Router 和 React Native Router 。如果你是创建的React Web应用,就使用React Web Router ,这里我们使用的就是这个Router,所以执行以下命令进行安装:
npm install --save react-router-dom
2. 在React项目中配置路由
想要在React项目中使用路由,必不可少的有两个组件(component): BrowserRouter 和 Route 。在React项目中,通过以下命令引入这两个组件:
import { BrowserRouter, Route } from "react-router-dom";
BrowserRouter 组件是React Router的核心,Route组件是React Router的具体配置。举个例子来说:BrowserRouter 组件就像是一个文具盒,Route组件就像是一件件文具一样,需要放在这个盒子里。这样我们在使用文具的时候,只需要打开文具盒,从里面取出即可。
了解了这层关系,我们就可以在React中配置路由了,配置非常简单,示例代码如下:
// React 应用中的 App.js 文件
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom'
import Set from './views/set/Set'
import Help from './views/help/Help'
class App extends React.Component {
render() {
return (
<BrowserRouter>
<Route path="/help" component={Help} />
<Route path="/set" component={Set} />
</BrowserRouter>
);
}
}
export default App;
在App.js文件中配置完车之后,然后在React入口文件中(一般是index.js文件),引入App.js文件并挂载到页面上,示例代码如下:
// React 应用中的入口文件 index.js
import React from 'react'
import { render } from 'react-dom'
import App from './App'
render((
<App />
), document.getElementById('root'))
挂载完成之后,通过npm run start
等命令启动本地开发服务器,就可以通过路由访问我们的React项目了。实例如下:
简单介绍一下以上代码中的路由配置规则,以这条路由配置为例:
<Route path="/help" component={Help} />
Help 是我们之前定义好的一个React组件,该组件的定义代码如下:
import React, { Component } from 'react'
class Help extends Component {
render() {
return (
<div className="help-box">
帮助与反馈 - 页面
</div>
);
}
}
export default Help;
当我们在浏览器地址栏中输入 http://localhost:3000/help
的时候,React Router 会匹配到 <Route path="/help" component={Help} />
这一条记录,然后就会在当前位置渲染对应的component。
注意是当前位置,说得更通俗一点:就是使用将Help组件中的代码替换掉 <Route path="/help" component={Help} />
代码,进而渲染出路由对应的component。关于这一点,后面会有更详细的解释。
3. 在React中通过链接跳转路由
通过上面的配置,我们已经可以通过输入URL来跳转路由,但是更多情况下,我们希望可以通过点击页面上的链接进行路由的跳转,比如以下的示例:
想要实现这一点,我们只需要引入 Link 这个组件即可,具体代码如下:
// React 应用中的 App.js 文件
import React from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom' // 引入 Link 组件
import Set from './views/set/Set'
import Help from './views/help/Help'
class App extends React.Component {
render() {
return (
<BrowserRouter>
{/* 注意 Link 组件一定要位于 BrowserRouter 组件中 */}
<ul>
<li><Link to="/help">Help</Link></li>
<li><Link to="/set">Set</Link></li>
</ul>
<Route path="/help" component={Help} />
<Route path="/set" component={Set} />
</BrowserRouter>
);
}
}
export default App;
三、React Router 的进阶使用
现在我们已经掌握了React Router的基本使用,但是这个时候我们掌握的知识还不足以让我们熟练地使用它,所以下面为大家介绍React Router更多内容。
1. Route 的匹配规则
在对Route进行配置的时候,我们可以传以下参数:path、exact、component、render、children 。这些参数都不是必须的,也就是说,我们有很多中配置Route的方式,常见的有以下几种:
<BrowserRouter>
<Route component={Wallet} />
<Route path="/" component={Index} />
<Route exact path="/" component={Index} />
<Route path="/help" component={Help} />
<Route path="/render" render={ () => { return <h1>我是匹配到的路由</h1> } } />
</BrowserRouter>
这里分别介绍以上五种Route的配置方式:
-
<Route component={Wallet} />
在配置Route的时候没有指定 path 属性,这时只要你打开项目,无论你访问什么路径,这个Route都会匹配到。
-
<Route path="/" component={Index} />
配置Route的时候,指定
path="/"
,和上面配置的效果一样,只要你打开项目,无论你访问什么路径,这个Route都会匹配到。这种配置方式我们成为 "非严格匹配" ,你在项目中访问的任何一个路径,里面都会包含path="/"
的路径。所以无论访问什么路径,这个Route都会匹配到。 -
<Route exact path="/" component={Index} />
和上面一条的配置方式不同,这里在配置Route的时候,传入了 exact 属性,说明这个配置方式是 "严格匹配" ,只有当我们访问项目的根路径的时候,才会匹配到这个Route。比如:localhost:3000/ 。
-
<Route path="/help" component={Help} />
一条很常规的Route配置方式,当我们访问"/help"路径的时候,匹配到这个Route,进而渲染出对应的component。
-
<Route path="/render" render={ () => { return <h1>我是匹配到的路由</h1> } } />
和上一条路由配置不同的是,这里没有component属性,而是换成了render属性。该属性是一个函数,当匹配到这个Route的时候,页面将会渲染出这个函数返回的内容。
2. Switch 组件的使用
Switch组件的使用其实非常简单,举个例子:如果说BrowserRouter组件是一个大的文具盒的话,那么Switch组件就是这个大文具盒中的一个小袋子,Route组件就是这个小袋子中的一支铅笔,我们需要铅笔的时候,直接从这个小袋子里面取出一支即可。注意:是最多只能取出一支铅笔。
也就是说,当进行路由匹配的时候,一旦匹配到了Switch组件中的一个Route,那么就不会再继续匹配。比如以下实例:
<BrowserRouter>
<Switch>
<Route component={Wallet} />
<Route path="/" component={Index} />
<Route path="/help" component={Help} />
<Route path="/render" render={() => { return <h1>我是匹配到的路由</h1> }} />
</Switch>
</BrowserRouter>
如果我们访问 "localhost:3000/help" 路径,虽然前三个Route都匹配,但是页面只会渲染第一个匹配到的Route,其他的都被忽略。看到这里你可能会问,这个组件有什么作用呢?可以在以下场景中发挥作用:如果当前路径没有匹配到任何Route的时候,路由跳转到 404 页面,那么就可以这样写:
<BrowserRouter>
<Switch>
<Route path="/set" component={Set} />
<Route path="/help" component={Help} />
<Route path="/render" render={() => { return <h1>我是匹配到的路由</h1> }} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
通过上面的配置,如果当前路径有匹配到最后一个之外的Route,那么就显示对应内容,不再继续匹配;如果没有匹配到最后一个之外的Route,就显示最后一个Route,因为这个Route可以匹配任何路径。可以把这个Route对应到 404 页面。
3. 匹配到的 Route 如何渲染内容
前面讲述了Route的配置和匹配规则,下面我们讲述一下,当路径匹配到Route之后,会如何渲染对应的内容。
之前有提到过:是在 当前位置 渲染内容,对于一些刚入门的小白来说,可能不能立刻理解当前位置是什么位置。所以我们举个例子,比如我们在项目的App.js组件中有以下配置代码:
class App extends React.Component {
render() {
return (
<div className="content">
<BrowserRouter>
<Route exact path="/" component={Index} /> {/* 位置 A */}
<Route path="/help" component={Help} /> {/* 位置 B */}
<Route path="/set" component={Set} /> {/* 位置 C */}
</BrowserRouter>
</div>
);
}
}
那么当路径为 "help" 的时候,就会匹配到第二个Route,那么React Router就会在 位置B 对应的这一行将 Route 组件替换成Help组件;而其他没有匹配到的Route,会被替换成null,相当于将这些 Route 组件直接删去。最终的结果如以下代码所示:
// Help 组件的定义代码可以在上文中找到
class App extends React.Component {
render() {
return (
<div className="content">
<div className="help-box">
帮助与反馈 - 页面
</div>
</div>
);
}
}
之所以讲这么一个知识点,是为后面介绍如何配置嵌套路由做准备,这里可以先提一下:如果我们想在 组件A中 嵌套几个子路由,那么就可以将这几个Route配置,写到组件A中,一旦路径匹配到了这些Route,就会在组件A中对应的位置渲染子路由对应的组件。听不懂没关系,后面会有详细的解释。
4. 对 React Router 的一点解释
通过上面的笔记,你可能会认为:Route组件必须位于BrowserRouter组件中,且两个组件必须在同一个文件中。
其实不然,从代码结构上说,Route组件确实必须位于BrowserRouter组件中,但是两个组件不一定非要在一个文件中,只要打包编译之后,两个组件满足Route组件位于BrowserRouter组件中就可以。比如以下的写法也是完全正确的:
// 项目的入口文件 index.js
import React from 'react'
import { render } from 'react-dom'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('root'))
// App.js 文件
import React from 'react';
import { Route, NavLink } from 'react-router-dom'
import Set from './views/set/Set'
import Help from './views/help/Help'
import Index from './views/index/Index'
import Wallet from './views/wallet/Wallet'
class App extends React.Component {
render() {
return (
{/* 注意这里已经没有了 BrowserRouter 组件 */}
<div className="content">
<ul>
<NavLink activeClassName="selected" to="help">help</NavLink >
<NavLink activeClassName="selected" to="index">index</NavLink >
<NavLink activeClassName="selected" to="wallet">wallet</NavLink >
<NavLink activeClassName="selected" to="set">set</NavLink >
</ul>
<Route path="/help" component={Help} />
<Route path="/index" component={Index} />
<Route path="/wallet" component={Wallet} />
<Route path="/set" component={Set} />
</div>
);
}
}
export default App;
5. 如何配置嵌套路由
嵌套路由,顾名思义就是在一个主路由匹配到的页面中再配置一些路由,我们现在就来讲述以下如何使用 React Router 配置嵌套路由,配置完成之后,我们会看到以下效果:
在之前的基础之上,我们已经配置好了一级路由,即可以通关点击 set 按钮跳转到匹配到的Route对应的组件中,即Set组件,具体配置可以参考上一个代码块中的代码。
下面我们要做的就是在Set组件中,进行配置二级路由,即嵌套路由。配置非常简单,这里先直接给出代码:
import React, { Component } from 'react'
import { Route, Link } from 'react-router-dom'
import SetSystem from '../SetSystem'
import SetPerson from '../SetPerson'
import SetTime from '../SetTime'
import './Set.scss'
class Set extends Component {
componentDidMount() {
// 当前页面匹配到的路径,这里是"/set"
// 使用这个路径,配置Route和Link
console.log(this.props.match.path);
}
render() {
return (
<div className="set-box">
个人设置 - 页面
<div className="link-list">
<Link to={`${this.props.match.path}/system`}>system</Link>
<Link to={`${this.props.match.path}/person`}>person</Link>
<Link to={`${this.props.match.path}/time`}>time</Link>
</div>
<div className="child-router">
<Route path={`${this.props.match.path}/system`} component={SetSystem} />
<Route path={`${this.props.match.path}/person`} component={SetPerson} />
<Route path={`${this.props.match.path}/time`} component={SetTime} />
</div>
</div>
);
}
}
export default Set;
注意代码中我们引入了 Set.scss 样式文件,给页面元素添加一些非常简单的样式,具体样式你可以自定义,这里的示例代码如下:
.set-box {
.link-list {
a {
margin: 0px 10px;
}
}
}
代码书写完成之后,我们就配置好了嵌套路由,启动项目你就可以看到上面GIF中展示的效果。这里对上面的配置做简单的解释:
首先再提一下:匹配到的Route会在 当前位置 渲染匹配到的内容(这个内容可能是component组件,render函数返回的结果等)。
就是因为React Router的这个特性,我们才能配置嵌套路由,否则,及时匹配到了Route,但是我们不知道它的渲染位置,也不能实现此功能。
其次再说一下 this.props.match
这个属性,通过这个属性,我们可以获得当前页面,即一级路由匹配到的组件对应的路径,在这里 this.props.match
的值是 "/set"。
了解了这一点,我们就可以在 this.props.match
的帮助下,设置二级路由的路径。所以以下两种写法是等价的,但是第一种写法更加灵活:
{/* 第一种写法 */}
<Link to={`${this.props.match.path}/system`}>system</Link>
<Route path={`${this.props.match.path}/system`} component={SetSystem} />
{/* 第二种写法 */}
<Link to="/set/system">system</Link>
<Route path="/set/system" component={SetSystem} />
最后再再说一下 Route组件 和 BrowserRouter组件 的位置问题。初次接触React Router的小白可能会问:在设置嵌套路由的时候,Route组件怎么没有包含在BrowserRouter组件中?
事实上,在使用webpack将代码打包打包编译之后,Route组件已经包含在BrowserRouter组件中了。在代码层面我们可以这样理解:Route组件包含于Set组件中,Set组件包含于App组件中,App组件包含在BrowserRouter中。所以Route组件已经包含在BrowserRouter组件中。
四、写在最后
啰啰嗦嗦写完了对React Router的总结,内容挺多但是都很基础,希望可以帮到刚刚入门的小白。
如果大神们看到错误之处,还希望各位能及时指正。