REACT-路由 V5
1. 安装路由
npm i react-router-dom@5
2. App.js引入路由并使用
import React, { Component } from 'react'
import {HashRouter,Route} from 'react-router-dom'//hash模式,路径中会带#号
import films from './views/Films'
import Cinemas from './views/Cinemas'
import Center from './views/Center'
export default class App extends Component {
render() {
return (
<div>
<HashRouter>
<Route path="/films" component={films} />
<Route path="/cinemas" component={Cinemas} />
<Route path="/center" component={Center} />
</HashRouter>
</div>
)
}
}
3. 路由组件封装
3.1 新建文件夹router => 新建路由组件文件IndexRouter.js
import React, { Component } from 'react'
import {HashRouter,Route} from 'react-router-dom'
import films from '../views/Films'
import Cinemas from '../views/Cinemas'
import Center from '../views/Center'
export default class IndexRouter extends Component {
render() {
return (
<div>
<HashRouter>
<Route path="/films" component={films} />
<Route path="/cinemas" component={Cinemas} />
<Route path="/center" component={Center} />
</HashRouter>
</div>
)
}
}
3.2 App.js中引入并使用路由组件
import React, { Component } from 'react'
import IndexRouter from './router/IndexRouter'
export default class App extends Component {
render() {
return (
<div>
其他内容
<div>
<IndexRouter />
</div>
</div>
)
}
}
4. 路由重定向
4.1 模糊匹配:路径中含有/,初次加载和刷新任意路由地址都会跳转到/films
import {HashRouter,Route,Redirect} from 'react-router-dom'
<HashRouter>
<Route path="/films" component={films}/>
<Route path="/cinemas" component={Cinemas}/>
<Route path="/center" component={Center}/>
{/* 模糊匹配,刷新页面后都会匹配到路径,下方有解决方案/*/}
<Redirect from="/" to="/films" />
</HashRouter>
4.2 Switch解决上面的路由匹配问题
- 使用switch进行包裹,只渲染匹配到的第一个路由
import {HashRouter,Route,Redirect,Switch} from 'react-router-dom'
<HashRouter>
<Switch>
<Route path="/films" component={films}/>
<Route path="/cinemas" component={Cinemas}/>
<Route path="/center" component={Center}/>
{/* 模糊匹配,刷新页面后都会匹配到路径/*/}
<Redirect from="/" to="/films" />
</Switch>
</HashRouter>
4.3 匹配不到路径,跳转notFound页面
- 精确匹配:exact-只有在/下会跳转到指定路径
import React, { Component } from 'react'
import {HashRouter,Route,Redirect,Switch} from 'react-router-dom'
import films from '../views/Films'
import Cinemas from '../views/Cinemas'
import Center from '../views/Center'
import NotFound from '../views/NotFound'
export default class IndexRouter extends Component {
render() {
return (
<div>
<HashRouter>
<Switch>
<Route path="/films" component={films}/>
<Route path="/cinemas" component={Cinemas}/>
<Route path="/center" component={Center}/>
{/* 模糊匹配,刷新页面后都会匹配到路径/*/}
<Redirect from="/" to="/films" exact/>//精确匹配:exact-只有在/下会跳转到指定路径
//当路径不存在时会自动跳转至notfound页面
<Route component={NotFound}/>
</Switch>
</HashRouter>
</div>
)
}
}
5. 嵌套路由
- 在同一页面下想展示不同组件时使用
- 嵌套路由需要在一级路由对应的组件内部进行编写
新建路由组件文件IndexRouter.js
import React, { Component } from 'react'
import {Route,Redirect,Switch} from 'react-router-dom'
import NowPlaying from '../views/flims/NowPlaying'
import CommingSoon from '../views/flims/CommingSoon'
export default class FilmsRouter extends Component {
render() {
return (
<div>
<Switch>
<Route path="/films/NowPlaying" component={NowPlaying}/>
<Route path="/films/CommingSoon" component={CommingSoon}/>
<Redirect from="/films" to="/films/NowPlaying" />
</Switch>
</div>
)
}
}
Films.js中引用该二级路由
import React from 'react'
import FilmsRouter from '../router/FilmsRouter'
export default function Films() {
return (
<div>
<div style={{background:'yellow',height:'300px'}}></div>
<FilmsRouter />
</div>
)
}
6. 声明式导航与编程式导航
6.1 声明式导航(NavLink)
- NavLink必须放在HashRouter标签中使用
- 选中导航后标签样式名默认添加active,可通过activeClassName修改选中样式名
- activeStyle-选中效果样式
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class Tabbar extends Component {
render() {
return (
<div>
<li>
<a href='#/films'>电影</a>
</li>
<li>
{/* 必须放在HashRouter标签中使用 */}
<NavLink to="/cinemas" activeClassName='activePart'>影院</NavLink>
</li>
<li>
<NavLink to="/center" activeStyle={{
fontWeight: "bold",
color: "red"
}}>我的</NavLink>
</li>
</div>
)
}
}
6.2 编程式导航(props.history.push)
import React,{useEffect,useState} from 'react'
import axios from 'axios'
import { NavLink,useHistory } from 'react-router-dom';
export default function NowPlaying(props) {
const [list,setList] = useState([]);
useEffect(()=>{
//异步获取数据
axios({
url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=9261499",
method:'get',
headers:{
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16745641013679850669801473","bc":"110100"}',
'X-Host': 'mall.film-ticket.film.list'
}
}).then(res=>{
setList(res.data.data.films)
})
},[])
const history = useHistory();
const handleChangePage = (id)=>{
// window.location.href = "#/detail/"+id;
// props.history.push("/detail/"+id);
history.push("/detail/"+id);
//类组件中使用this.props.history.push(`/路径${id}`)进行跳转
}
return (
<div>
{list.map(item => {
return (
// <div key={item.filmId}>
// <NavLink to={'/detail'+item.filmId}>{item.name}</NavLink>
// </div>
<div key={item.filmId} onClick={() => handleChangePage(item.filmId)}>{item.name}</div>
)
})}
</div>
)
}
7. 路由传参
建议使用动态路由,参数不会丢失
7.1 params(动态路由传参)
优势 : 刷新,参数依然存在
缺点 : 只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。
<Route path="/detail/:id" component={Detail}/>
this.props.history.push("/detail/"+id);
this.props.match.params.id
7.2 query
优势:地址栏不显示参数,传递参数可传对象;
缺点:刷新地址栏,参数丢失
<Route path="/detail" component={Detail}/>
this.props.history.push({pathname:'/detail',query:{id:id}});
this.props.location.query.id
7.3 state
同query差不多,只是属性不一样,而且state传的参数是加密的,query传的参数是公开的,只需要把query改为state即可。
优势:地址栏不显示参数,传递参数可传对象
缺点:刷新地址栏,(hash方式会丢失参数,Browser模式不会丢失参数)
<Route path="/detail" component={Detail}/>
this.props.history.push({pathname:'/detail',state:{id:id}});
this.props.location.state.id
7.4 search
优势:地址栏显示参数,舒心不会丢失
缺点:只能传递字符串
<Route path="/detail" component={Detail}/>
this.props.history.push({pathname:'/detail',search:'?a=1&b=2'})
this.props.location.search
用location.search所获取的是查询字符串(?a=1&b=2),可以使用nodejs里的qs(原名querystring)
import React from 'react'
import qs from 'qs'//不用安装,直接引用
export default function Detail(props) {
let search = props.location.search.slice(1);//去除?
let newSearch = qs.parse(search);
return (
<div>
{/* Detail-{props.match.params.id} */}
{/* Detail-{props.location.query.id} */}
{/* Detail-{props.location.state.id} */}
Detail-{newSearch.id}
</div>
)
}
8. 路由拦截
进入我的信息页面前进行是否登录判断,再渲染不同的组件页面
注意:render方法渲染的组件需要层层传递props属性,这样在在center组件中可以使用路由的相关api,center中的子组件想要用路由api,也需要父组件传递props
<Route path="/center" render={(props)=>{
return isAuth() ? <Center {..props}/> : <Redirect to="/login"/>
}}/>
<Route path="/login" component={Login}/>
9. 路由模式
- HashRouter 地址含有#
- BrowserRouter 地址不含# ,好看,会向后端发送请求要页面,如果后端没有对应的路径处理,就会报404错误
- as 可以将引入模块进行重命名
import {HashRouter as Router } from 'react-router-dom'
<Router>
</Router>
10. withRouter
使用withRouter,组件内部会继承父组件的props,高阶组件原理
//路由
<Route path="/center" render={(props)=>{
return isAuth() ? <Center/> : <Redirect to="/login"/>
}}/>
//Center组件
import React,{Component} from 'react'
import { withRouter } from 'react-router-dom'
// function Center (props){
// return (
// <div>
// Center
// <div onClick={()=>{
// props.history.push('/filmsOrder');
// }}>电影订单</div>
// </div>
// )
// }
// export default withRouter(Center)
class Center extends Component {
render() {
return (
<div>
Center
<div onClick={()=>{
this.props.history.push('/filmsOrder');
}}>电影订单</div>
</div>
)
}
}
export default withRouter(Center)
import React,{useEffect,useState} from 'react'
import axios from 'axios'
import { NavLink,useHistory,withRouter } from 'react-router-dom';
export default function NowPlaying(props) {
const [list,setList] = useState([]);
useEffect(()=>{
//异步获取数据
axios({
url:"https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=9261499",
method:'get',
headers:{
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16745641013679850669801473","bc":"110100"}',
'X-Host': 'mall.film-ticket.film.list'
}
}).then(res=>{
setList(res.data.data.films)
})
},[])
return (
<div>
{list.map(item => {
return <WithFilmItem key={item.filmId} {...item}/>
})}
</div>
)
}
function FilmItem(props){
const handleChangePage = (id)=>{
//1-动态路由传参(推荐使用,参数不会丢失)
props.history.push("/detail/"+id);
// <Route path="/detail/:id" component={Detail}/>
// props.match.params.id
}
return <div onClick={() => handleChangePage(props.filmId)}>{props.name}</div>
}
const WithFilmItem= withRouter(FilmItem);
11. 路由组件api
// push: a-b-c 可以回到上一级
//replace: a-b-c 回不到上一级 适用于登录后,不需要重新回到登录页面
this.props.history.replace('/center')
// 前进
this.props.history.goForward();
// 回退
this.props.history.goBack();
// 向前或向后跳转制定步数
this.props.history.go(1);