React Router 路由管理详解
December 19, 2024 (1y ago)
在这一小节,我们将学习到最重要的两部分核心内容
- react-router路由
- react-redux状态管理,类似于vuex
- 这两个东西都是非常的重要的东西
react-router
与vue-router一样,react中也是有router的,接下来我们来学习看看这个到底是怎么操作的
基础的使用
基础的使用非常的简单,我们使用官方推荐的一个库,来进行路由管理
- react-router 核心组件
- react-router-dom 应用于浏览器端的路由库(单独使用包含了react-router的核心部分)
- react-router-native 应用于native端的路由
yarn add react-router-dom
# 或者,不使用 yarn
npm install react-router-dom- 由于最新的react-router更新了,所以不再需要单独的配置
- Router是所有路由组件共用的底层接口组件,它是路由规则制定的最外层的容器。
- Route路由规则匹配,并显示当前的规则对应的组件。
- Link路由跳转的组件

小结:如果说我们的应用程序是一座小城的话,那么Route就是一座座带有门牌号的建筑物,而Link就代表了到某个建筑物的路线。有了路线和目的地,那么就缺一位老司机了,没错Router就是这个老司机。
- 安装完毕之后我们来使用一下
导入三个核心的东西,注意啊我们这里是起别名的
- 使用Router组件包裹整个应用

- 使用link作为点击跳转的(入口)

- 使用Route配置(出口)

注意啊,这里的这个这个path就是配置的,component就是指定匹配的时候要显示的路由,link的to和Route的path是要匹配的

常用组件的说明
一个React应用中只需要使用一次,除了我们之前说的BrowserRouer我们还有一个HashRouter效果是一样的,只是后面加了一个#符号
Link最终变成a标签,href就变成了to的属性,to也是url里的东西,也是可以更改的,
Route是出口,占位符,path是一个规则,注意如果有一堆Router那么一个Link会去匹配所有的Route的paht直到找到匹配成功为止
整个流程到底是什么呢?
只要rul发生变化,整个路由都会重新匹配一次

如何实现编程式导航?
非常的简单,我们之前说过React中的prop是一个核心,如果一个组件是被Route占位显示出来的,意味着这个组件内部的prop上就有一个history属性,通过它就能完成url改变 ,实现导航

history有相关的如下的一些api
默认的路由如何展示?展示还是非常的简单
只需要使用一个path = '/'就好了。/就是默认的路由,进入页面就会被匹配的路由,
精确匹配
以上都是最基础的使用步骤,当然关关是基础的使用是远远不足够我们做项目的,我们需要更多的深入学习react-router
如何实现路由的参数传递呢?
比如,你点击某一个具体的文件的时候 ,我需要你这篇文章的id那么你如何传递这个id给我呢?我又要如何获取?props.params 是一种解决方案
1、目录及 组件关系图
组件关系:

2、源码
./index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App/App';
ReactDOM.render(<App />, document.getElementById('root'));
./src/components/App/App.js
import React , { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import About from '../About/About';
class App extends Component {
constructor() {
super();
this.state = {
lists: ['10010', '10086', '8000'], // 在react中万物皆组件,万物皆组件,跟我默念,万物皆组件!!!!!
};
}
render() {
let linkList = this.state.lists.map(item => {
return (
<li><Link to={`/about/${item}`}> {item} </Link></li>
);
});
return (
<Router>
<div>
<ul> { linkList } </ul>
<Route path="/about/:tel" component={About} />
</div>
</Router>
);
}
}
export default App;
注:< Route>在选择渲染About组件时,其实也传给了About组件一个 match对象。在About中直接this.props.match即可访问。
这句话的意思就是,在路由跳转的时候,你传递了一个match对象在props上,这个match就是路由对象
./src/components/About/About.js
import React, { Component } from 'react';
class About extends Component {
render() {
return (
<div>about: {this.props.match.params.tel}</div>
);
}
}
export default About;

点击8000后:

第二种传参扥方式
使用query定义传值方式,注意:页面刷新后,参数丢失
<Route path="/query" component={App} />
//在Link组件中定义参数
const param ={
pathname:"/query",
query:"参数"
}
<Link to={param}>List</Link>
//在子组件中获取参数值
this.props.location.query第三种解决方案
以上仅仅是我们的点击的时候传递的值,在rul上可以拿到,但是如果我们要编程式导航,那么如何拿到我们的值呢?
- 入股是history.repacle( 目标path,{})第二个就是你需要传递过去的对象,在目标path组件内通过this.props上有一个location能拿到第二个对象(你传递过去的值)
第四中解决方案(推荐)
可以有效的解决页面刷新后,参数丢失的问题
- linke定义
let obj = {
pathname: '/component',
search: '?sort=name',
query: { fromDashboard: 123 },
state: { fromDashboard: 123 }
}
<Link to="{obj}">Zillow Group</Link>- 接收
this.props.location.search // search == "?sort=name"
this.props.location.query // query == { fromDashboard: 123 }
this.props.location.state // state == { fromDashboard: 123 }如何实现多层级的页面嵌套呢?实际上也非常的简单
1、目录及 组件关系图
组件关系:

2、源码
./index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App/App';
ReactDOM.render(<App />, document.getElementById('root'));./src/components/App/App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Home from '../Home/Home';
import About from '../About/About';
import Inbox from '../Inbox/Inbox';
function App() {
return (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/inbox">Inbox</Link></li>
</ul>
<hr/>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/inbox" component={Inbox} />
</div>
</Router>
);
}
export default App;
./src/components/About/About.js
import React, { Component } from 'react';
class About extends Component {
render() {
return (
<div>i am about</div>
);
}
}
export default About;./src/components/Inbox/Inbox.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
class Inbox extends Component {
render() {
return (
<div>
<h2>Inbox title</h2>
<ul>
<li><Link to={`${this.props.match.url}/components`}> Components </Link></li>
<li><Link to={`${this.props.match.url}/props-v-state`}> Props v. State </Link></li>
</ul>
<Route path={`${this.props.match.path}/:getId`} component={Topic} />
<Route path={this.props.match.path} exact render={() => <h3>Please select a topic.</h3>}/>
</div>
);
}
}
function Topic(props) {
return <h3>{props.match.params.getId}</h3>;
}
export default Inbox;


3、展示

点击Inbox后:

点击 Props v. State 后:

如何把路由独立出来?(手写实现,当然你也可以使用router-config这个来搞)
目前啊,我发现现在的router耦合度太高了,如果我想要把他们我说的是router独立出来操作,那么我要如何做呢?
实际上还是有些复杂的,我们来看需求
- 需求,我希望
import home from 'pages/Home.jsx'
import record from 'pages/Record.jsx'
import feedBack from 'pages/FeedBack.jsx'
const router = [
{
path:'/record',
component:record
},
{
path:'/',
component:home,
redirect:'/feedBack',
children:[
{
path:'/feedBack', ////就是这里vue的话会写成feedBack,但是react会/
component:feedBack
}
]
},
]
//如果熟悉vue的小伙伴会明白想干什么,
// 主要是在/路径下生成home但是匹配到/之后会重新定义到/feedBack路径上去,
//此时的home和feedBack组件是共存的,也就是常说的layout和contain是共存的,如果是vue的话很简单,很好做,但是react的路由我*****- 实现的步骤如下
我们使用了router 对路由数据进行了构建,下面我只需要一个方法生成他,手写!但是没有必要,我们后续会介绍一个router结合使用的一个库,利用那个库,可以优雅的实现动态路由生成
// 生成动态路由的方法
function generateRoute(value){
if(value.children){ // 如果有子组件
return (
<Switch key={value.path}>
<Redirect exact from={value.path} to={value.redirect} />
<Route key={value.path} path={value.path}>
<value.component>
{
value.children.map(item=>{
return generateRoute(item)
})
}
</value.component>
</Route>
</Switch>
)
}
// 如果没有子组件
return <Route key={value.path} path={value.path} component={value.component} />
}
// 组件
function app(){
return (
<Switch>
{
// 重点来了!
router.map(item=>{
return generateRoute(item)
})
}
</Switch>
)
}
// 在一系列的转化下他会生成这个东西
<Switch>
<Route path='/record' component={record}></Route>
<Switch>
<Redirect exact from='/' to='/feedBack'></Redirect>
<Route path='/'>
<Home>
{
<Route path='/feedBack' component={feedBack} />
}
</Home>
</Route>
</Switch>
</Switch>
//在这里我们看到了<Home>组件,这样在Home组件中我们可以通过this.props.children来获取传入的组件,类似于vue的插槽
相关技术点说明:
在上述的例子中,我们的这个发现有两个貌似没有接触过的东西**Switch**** 和Redirect**接下来我们来看看他们到底是干什么用的
- switch
字面上非常好理解,这个就是一个开关,同一个时间点内只匹配一个
有
- reditect
字面上非常好理解,重定向!,这个没有什么需要说明的,重定向!重什么地方跳到什么地方去
<Redirect from="messages/:id" to="/messages/:id" />使用第三方库来实现 路由的配置抽离
在vue中,经常会有 路由拦截件的东西,在react中如何做到呢?
答案就是
react-router-config
npm i react-router-dom react-router-config –save- react可以通过一款react-router-config插件做到和vue-router一样的使用块感
- 搞一个home页面
import React from 'react';
export default function Home(){
return (
<div>
<h1>这是主页</h1>
<a href="/about">去about</a>
<a href="/discover">去discover</a>
</div>
)
}- 建立一个about页面
import React from 'react';
import {Link} from 'react-router-dom';
import {renderRoutes} from 'react-router-config';
//变量的导出用export,而不是export default
export const About = (props)=>{
const route = props.route
return (
<div>
<h1>这是关于</h1>
<a href="/">去home</a>
<a href="/discover">去discover</a>
<Link to="/about/my">关于我的</Link>
<br />
<Link to="/about/you">关于你的</Link>
{/*使用renderRoutes方法继续渲染子路由,第二个参数可以进行传参*/}
{renderRoutes(route.childrens,{user:'hello'})}
</div>
)
}- 建立一个discover页面
import React from 'react';
import {Link} from 'react-router-dom';
import {renderRoutes} from 'react-router-config';
export default function Discover(props){
const route = props.route
return (
<div>
<h1>这是发现</h1>
<a href="/">去home</a><br />
<a href="/about">去about</a><br />
<Link to="/discover/earth">发现地球</Link>
<br />
<Link to="/discover/star">发现星星</Link>
{renderRoutes(route.childrens)}
</div>
)
}- 再建立两个子页面
my
import React from 'react';
export default function My(props){
return (
<div>
<h1>这是关于我的</h1>
<p>{props.user}</p>
</div>
)
}you
import React from 'react';
export default function You(){
return (
<div>
<h1>这是关于你的</h1>
</div>
)
}- 我们还需要一些辅助页面
Star
import React from 'react';
export default function Star(){
return (
<div>
<h1>这是发现星星</h1>
</div>
)
}Earth
import React from 'react';
//<p>{props.match.params.id}</p>
export default function Earth(props){
return (
<div>
<h1>这是发现地球</h1>
</div>
)
}- 建立配置文件
router.js
在src目录下新建文件夹route(名字自己随便取),然后在该目录下新建route.js文件,在route.js里进行文件的配置
import Home from '../component/home'
//注意非函数、非表达式等导入要用{}括住
import {About} from '../component/about'
import Discover from '../component/discover'
import Earth from '../component/earth'
import Star from '../component/star'
import My from '../component/my'
import You from '../component/you'
const routes = [
{
path:'/',
component: Home,
exact:true
},
{
path:'/about',
component: About,
childrens:[
{
path:'/about/my',
component:My
},
{
path:'/about/you',
component:You
}
]
},
{
path:'/discover',
component: Discover,
childrens:[
{
path:'/discover/earth',
component:Earth
},
{
path:'/discover/star',
component:Star
}
]
}
]
export {routes}- 最后的最后我们总算来渲染了
index,js
import React from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter} from "react-router-dom";
import { renderRoutes } from "react-router-config";
import './index.css';
import {routes} from './router/route';
// 使用renderRoutes方法渲染路由
ReactDOM.render(<BrowserRouter>
{ renderRoutes(routes) }
</BrowserRouter>, document.getElementById('root'));
serviceWorker.unregister();以上就是一个非常简单的库的基础使用在里面
如何做路由守卫的拦截和权限的判断呢?
非常简单,使用router-config只需要改一些东西就好,什么?💩吧,这要手写一个鉴权 ,没事哥扛得住!
由于这里的这一块内容相对的比较难,需要学习redux之后才能读得明白,那么我们先去学习redux后续回过头开看这个实现的技术解决方案
关于路由鉴权和动态路由匹配的这个问题是非常值得一提的,因为在业务中会经常的使用到,所以啊,这个非常的重要!我们在学习完redux之后需要额外的认真学习,该需求的解决方案
/ ---2021-12-10日更新,目前针对这一块的解决方案如下,也是目前老李在企业中的运用

