React路由分为三类,WEB,NATIVE,ANYWHERE,根据名字就可以知道,第一种主要用于web开发,第二种主要运用于native开发,第三种这哪种情况下都能用,虽然第一种和第三种都能运用于web开发,但第一种更合适。以下内容介绍第一种。
一,安装react路由
使用路由要先在脚手架里安装。
npm i react-router-dom
react-router-dom是react的一个插件库。
二,路由的使用
react项目是一个单页面项目,其中的内容由组件构成。点击页面中的链接不会刷新页面,只会做页面的局部更新。
1,基础用法
下面代码的结构:
在App.js中注册了两个路由,跳转到Teacher组件和Student组件, (基础使用);
在Student组件中注册了两个路由,跳转到Message组件和Grade组件,(嵌套路由);
在Grade组件中动态注册路由,跳转到Detail组件中,Detail组件展示数据,(路由传参)。
App.js
import React, { Component } from 'react'
import {Route, NavLink, Switch, Redirect} from 'react-router-dom'
import Teacher from './pages/Teacher'
import Student from './pages/Student'
import Student2 from './pages/Student2'
import './index.css'
export default class App extends Component {
render() {
return (
<div>
<div>
<div >
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<NavLink activeClassName="highlight" className="btn" to="/teacher">Teacher</NavLink>
<NavLink activeClassName="highlight" className="btn" to="/student">Student</NavLink>
</div>
{/* 注册路由 */}
<Switch>
<Route path="/teacher" component={Teacher}/>
<Route path="/student" component={Student}/>
<Route path="/student" component={Student2}/>
{/* <Redirect to="/teacher"/> */}
</Switch>
</div>
</div>
)
}
}
Teacher
import React, { Component } from 'react';
export default class Teacher extends Component {
render() {
return (
<div>
<h3>Teacher</h3>
</div>
);
}
}
分为下面三个步骤:
(1)先要从react-router-dom这个库中引入需要使用的内容。
(2)定义路由,编写路由链接,<NavLink></NavLink>编写,to=""表示路由的路径。
通过activeClassName指定样式名。给它添加了一个高亮的效果。
上面是导航,下面是组件中的内容。
(3)注册路由,path=""表示路径,component表示需要显示的组件。
<Switch>:通常情况下,path和component是一一对应匹配的,Switch能够单一匹配。
如果有两个组件对应同一个path,不加Switch时,两个组件都会显示。
加上Switch时,只会匹配第一个。
<Redirect />:代表重定向,当所有路由都无法匹配时,跳转到Redirect指定的路由。一般用在页面刚加载时,页面应该显示一个组件。
(4)最后,要用<BrowserRouter>或<HashRouter>将路由包起来,一个项目中需要路由的地方很多,react脚手架只显示App.js一个组件,所以直接包裹在<App>外侧。
2,嵌套路由
在Student组件中再添加两个路由,App.js中的内容和上面的一样。
Student组件
import React, { Component } from 'react';
import { NavLink, Route, Switch, Redirect } from 'react-router-dom'
import Message from './Message';
import Grade from './Grade';
export default class Student extends Component {
render() {
return (
<div>
<h3>Student</h3>
<div>
<NavLink activeClassName="highlight" className="btn" to="/student/message">Message</NavLink>
<NavLink activeClassName="highlight" className="btn" to="/student/grade">Grade</NavLink>
{/* 注册路由 */}
<Switch>
<Route path="/student/message" component={Message}/>
<Route path="/student/grade" component={Grade}/>
<Redirect to="/student/message" />
</Switch>
</div>
</div>
);
}
}
Grade组件
import React, { Component } from 'react';
export default class Grade extends Component {
render() {
return (
<div>Grade</div>
);
}
}
注册子路由时要写上父路由的path值。to="/student/message",/student是父路由,/message是子路由。
路由的匹配是按照注册路由的顺序进行的。
3,运用路由传递参数
组件分为一般组件和路由组件;
(1)写法不同:
一般组件:<Student />
路由组件:<Route path="/student" component={Student}/>
(2)传递的参数不同:
一般组件:在组件标签里写什么就会传递什么。
第一张图片什么都不传,第二张图片传a=1
路由组件:会接收到三个固定的属性,history,location,match。
withRouter
给一般组件添加withRouter,一般组件也可以使用路由组件的三个固定属性。
传递参数有三种方法,携带params参数,携带search参数,携带state参数。
每种方法都有三个步骤:
(1),路由链接,携带参数
(2),注册路由,声明接收参数
(3),在子组件接收参数
携带params参数
Grade组件
import React, { Component } from 'react';
import {Link,Route} from 'react-router-dom'
import Detail from './Detail';
export default class Grade extends Component {
state = {
nameArr: [
{id:'01', name:'zs'},
{id:'02', name:'ls'},
{id:'03', name:'ww'},
]
}
render() {
const {nameArr} = this.state
return (
<div>
<ul>
{
nameArr.map((msgObj)=>{
return (
<li key={msgObj.id}>
{/* 携带params参数 ,都能写对象,只写一个路径*/}
<Link to={`/student/grade/detail/${msgObj.id}/${msgObj.name}`}>{msgObj.name}</Link>
</li>
)
})
}
</ul>
<hr />
{/* 声明接收params */}
<Route path="/student/grade/detail/:id/:name" component={Detail} />
</div>
);
}
}
Detail组件
import React, { Component } from 'react';
const GradeData = [
{id: '01', grade:'70'},
{id: '02', grade:'80'},
{id: '03', grade:'90'},
]
export default class Detail extends Component {
render() {
console.log(this.props)
// 接收params参数
const {id, name} = this.props.match.params
// 传过来的id和Grade种的数据比较
const nameGrade = GradeData.find((detailObj)=>{
return detailObj.id === id
})
return (
<div>
<div>name:{name}</div>
<div>grade:{nameGrade.grade}</div>
</div>
);
}
}
这种方法会将参数带到页面的地址栏中。
(1),携带参数;
<Link to={`/student/grade/detail/${msgObj.id}/${msgObj.name}`}>{msgObj.name}</Link>
(2),声明接收;
<Route path="/student/grade/detail/:id/:name" component={Detail} />
(3),子组件接收参数,要先找到携带的参数在哪儿;
const {id, name} = this.props.match.params
携带search参数
Grade组件
import React, { Component } from 'react';
import {Link,Route} from 'react-router-dom'
import Detail from './Detail';
export default class Grade extends Component {
state = {
nameArr: [
{id:'01', name:'zs'},
{id:'02', name:'ls'},
{id:'03', name:'ww'},
]
}
render() {
const {nameArr} = this.state
return (
<div>
<ul>
{
nameArr.map((msgObj)=>{
return (
<li key={msgObj.id}>
{/* 携带search参数 */}
<Link to={`/student/grade/detail?id=${msgObj.id}&name=${msgObj.name}`}>{msgObj.name}</Link>
</li>
)
})
}
</ul>
<hr />
{/* search参数无需声明接收 */}
<Route path="/student/grade/detail" component={Detail} />
</div>
);
}
}
Detail组件
import React, { Component } from 'react';
import qs from 'qs'
const GradeData = [
{id: '01', grade:'70'},
{id: '02', grade:'80'},
{id: '03', grade:'90'},
]
export default class Detail extends Component {
render() {
console.log(this.props)
// 接收search参数
const {search} = this.props.location
const {id, name} = qs.parse(search.slice(1))
// 传过来的id和Grade种的数据比较
const nameGrade = GradeData.find((detailObj)=>{
return detailObj.id === id
})
return (
<div>
<div>name:{name}</div>
<div>grade:{nameGrade.grade}</div>
</div>
);
}
}
这种方法也会在页面的路径中显示传递的内容。
(1),携带参数;
<Link to={`/student/grade/detail?id=${msgObj.id}&name=${msgObj.name}`}>{msgObj.name}</Link>
(2),声明接收,search不需要声明接收;
<Route path="/student/grade/detail" component={Detail} />
(3),接收参数,接收到的是字符串格式的,需要使用一个第三方库qs进行转化;
const {id, name} = this.props.location.search
携带state参数
Grade组件
import React, { Component } from 'react';
import {Link,Route} from 'react-router-dom'
import Detail from './Detail';
export default class Grade extends Component {
state = {
nameArr: [
{id:'01', name:'zs'},
{id:'02', name:'ls'},
{id:'03', name:'ww'},
]
}
render() {
const {nameArr} = this.state
return (
<div>
<ul>
{
nameArr.map((msgObj)=>{
return (
<li key={msgObj.id}>
{/* 携带state参数,这个需要写state,所以只能写对象 */}
<Link to={{pathname:'/student/grade/detail', state:{id:msgObj.id, name:msgObj.name}}}>{msgObj.name}</Link>
</li>
)
})
}
</ul>
<hr />
{/* state参数无需声明接收 */}
<Route path="/student/grade/detail" component={Detail} />
</div>
);
}
}
Detail组件
import React, { Component } from 'react';
const GradeData = [
{id: '01', grade:'70'},
{id: '02', grade:'80'},
{id: '03', grade:'90'},
]
export default class Detail extends Component {
render() {
console.log(this.props)
// 接收state参数
const {id, name} = this.props.location.state
// 传过来的id和Grade种的数据比较
const nameGrade = GradeData.find((detailObj)=>{
return detailObj.id === id
})
return (
<div>
<div>name:{name}</div>
<div>grade:{nameGrade.grade}</div>
</div>
);
}
}
这种方法携带的参数不会显示到页面的地址栏中。
(1),携带参数;
<Link to={{pathname:'/student/grade/detail', state:{id:msgObj.id, name:msgObj.name}}}>{msgObj.name}</Link>
(2),声明接收,state方法也不需要声明接收;
<Route path="/student/grade/detail" component={Detail} />
(3),接收参数;
const {id, name} = this.props.location.state
传递参数小总结:
(1),params方法和search方法会将携带的参数显示在地址栏中,state方法不会。
(2),params方法需要声明接收参数,search方法和state方法不需要声明接收,正常注册路由。
(3),三种方法刷新都可以保留参数,数据不会消失。
4,编程式路由导航
编程式路由导航是借助this.props.history对象上的API操作路由跳转,前进,后退等。
import React, { Component } from 'react';
export default class Student extends Component {
pushTeacher = () =>{
this.props.history.push('/teacher')
}
render() {
// console.log(this.props)
return (
<div>
<h3>Student</h3>
<button onClick={this.pushTeacher}>跳转到Teacher</button>
</div>
);
}
}
常用的有
push():历史记录是按栈的形式存放的,push方法在原先的基础上加入一条记录;
replace():会将原先记录栈顶的一条记录替换掉;
goBack():后退一条记录;
goForward():前进一条记录;
go():添加数字,1代表前进一条记录,-1代表后退一条记录。