一个路由就是一个映射关系(key:value)
key为路径, value可能是function或component
在react中使用 react-router-dom(插件) 来进行路由的配置
使用npm i react-router-dom
命令下载 路由插件;
npm i [email protected]
下载6以下的版本在下载了 react-router-dom 插件之后,就可以通过 react-router-dom包引入 Route
组件注册路由。
Route组件包含以下属性
path
: 路由的路径component
: 组件exact
: 是否进行严格匹配(默认模糊匹配)以下就注册了一个 Home 组件 ,组件路径为/home。
// [1]引入注册路由组件
import {Route} from 'react-router-dom'
// [2] 引入组件
import Home from '../src/pages/Home'
// [3] 注册组件
<Route path='/home' component={Home}></Route>
在注册路由时如下:可能有两个甚至多个的路由注册,通过 下面代码 注册路由 当path变为/home时 路由出口的位置展示哪个组件-- Home组件orTest组件or两个都展示?
<Route path='/about' component={About}/>
<Route path='/home' component={Home}/>
<Route path='/home' component={TEst}/>
从第一个开始匹配直至最后一个,即使找到对应路由也不会停止寻找
;使用switch包裹后的匹配规则: 从第一个开始寻找,找到一个对应的路由则匹配结束
;通过 下面代码 注册路由 当path变为/home时仅仅展示Home组件
<Switch>
<Route path='/about' component={About}/>
<Route path='/home' component={Home}/>
<Route path='/home' component={Nested}/>
</Switch>
组件的渲染存在三种方式
若是以上三个属性同时存在 则优先级 children > component > render
component属性用于渲染对应组件,属性值为一个组件
<Route path='/about' component={About}/>
render属性也是用于渲染组件,属性值为一个函数,一般用在一些渲染组件的之前或者之后做一些事情,例如 一些需要登陆权限之类的受保护的组件。
<Route path='/about' render={(props)=>{
if(token){
return <About />
}else{
return <Redirect to='/login' />
}
}}/>
children用法跟 render 一样,区别在于,就算不匹配路由,children 函数也会触发;
在下载了 react-router-dom 插件之后,就可以通过 react-router-dom包引入 Link
、NavLink
组件进行导航(路由跳转)。
Link组件包含以下属性
to
: 点击之后需要跳转的路由路径replace
:是否覆盖当前路径NavLink组件包含以下属性
to
: 点击之后需要跳转的路由路径activeClassName
: 点击之后的样式的类名,默认值为activereplace
:是否覆盖当前路径以下就注册了一个 Home 组件 ,组件路径为/home;配置了一个跳转到home组件的链接,当点击“点我跳转到home组件”页面路径就变为/home了。
import {Component} from 'react'
import Home from '../src/pages/Home'
import {Link, Route} from 'react-router-dom'
export default class App extends Component {
render(){
return (
<div>
<Link to='/home'>点我跳转到home组件</Link>
<Route path='/home' component={Home}></Route>
</div>
)
}
}
除了上述通过组件进行路由跳转,也可以通过编程式导航进行路由跳转;
语法-借助prop.history
对象上的API对操作路由跳转、前进、后退
this.prop.history.push()
this.prop.history.replace()
this.prop.history.goBack()
this.prop.history.goForward()
this.prop.history.go()
const domA = document.createElement('a')
domA.href = history.createHref({ pathname: url })
domA.target = '_blank'
domA.click()
因为路径上携带参数是添加到props中的,而props值改变不会重新渲染页面
,因此在跳转到页面之后,我们可以在useEffect中直接 跳转即可
useEffect(()=>{
goodsList()
// 若是存在参数,直接跳转到当前页面
if(searchparams.order_sn){
props.history.push('/admin/sellorder')
}
},[])
路由分为两类:
Browser
路由: 路径和正常路径相同(与vue中history路由原理相同)Hash
路由:路径中存在#(与vue中hash路由原理相同)路由出口
;
;下面配置表示该项目使用的是 Browser路由:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
下面配置表示该项目使用的是 Hash路由:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { HashRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<HashRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</HashRouter>
);
<Switch>
<Route path='/about' component={About}/>
<Route path='/home' component={Home}/>
<Route path='/nested' component={Nested}/>
</Switch>
以上是注册的几个路由,但是一进入页面时,匹配不到任何的路由,此时若是我们想进入页面(url为空)时默认展示home组件的内容,可以使用路由重定向。
Redirect组件包含以下属性
to
: 路由重定向的路径在下载了 react-router-dom 插件之后,就可以通过 react-router-dom包引入 Redirect
组件进行路由重定向。
Redirect写在注册路由的下方(兜底)—当所有路由都匹配不上时 自动跳转到Redirect设置的路径上。
<Switch>
<Route path='/about' component={About}/>
<Route path='/home' component={Home}/>
<Route path='/nested' component={Nested}/>
<Redirect to='/home' />
</Switch>
在上方路径匹配后,当在浏览器输入路径 http://localhost:3000,默认路径会变为 http://localhost:3000/home
在路由跳转时,我们需要将 Link组件的to属性值 与 Route组件的path属性值进行匹配
只要path属性值匹配完毕且全部对应
(to属性值存在剩余没有关系)则称为匹配模糊匹配举例说明
<Link to='/home'></Link>
<Route path='/home' component={Home}/>
可以成功跳转
<Link to='/home/a/b'></Link>
<Route path='/home' component={Home}/>
可以成功跳转
<Link to='/a/home/a/b'></Link>
<Route path='/home' component={Home}/>
不能成功跳转
注意:不要随意开启严格匹配,否则会出现一些问题 比如嵌套路由无法匹配
首先明确一点:路由的匹配是按照注册路由的顺序进行
的.
举例说明
在App.vue(跟组件)注册了 Home路由和About路由,在Home组件注册了 Sun1和Sun2 两个子路由
import About from '../src/pages/About'
import Home from '../src/pages/Home'
import {Link,NavLink, Route,Switch,Redirect} from 'react-router-dom'
import {Component} from 'react'
export default class App extends Component {
render(){
return (
<div>
<div className='left'>
<Link to='/about'>About</Link>
<br />
<NavLink activeClassName='active' to='/home'>Home</NavLink>
<br />
<Link to='/home/sun1'>sun1</Link>
<br />
<Link to='/home/sun2'>sun2</Link>
</div>
<div className='right'>
<Switch>
<Route path='/about' component={About}/>
<Route path='/home' component={Home}/>
<Redirect to='/home' />
</Switch>
</div>
</div>
)
}
}
import React, { Component } from 'react'
import {Route,Switch} from 'react-router-dom'
import Sun1 from './New'
import Sun2 from './New2'
export default class Home extends Component {
render() {
return (
<div>
我是home组件
<Switch>
<Route path='/home/sun1' component={Sun1}></Route>
<Route path='/home/sun2' component={Sun2}></Route>
</Switch>
</div>
)
}
}
注册子路由时要写上父路由的path值
不然最初的匹配就失败!通过路由向组件传参一共有三种方式
<Link to='url?参数名=参数值&参数名=参数值'></Link>
this.props.location.search // String
<Link to='/home/sun1?name=111&age=18'>sun1</Link>
在子组件接收console.log('search参数', this.props.location.search) // search参数 ?name=111&age=18
接收到的是 urlencoded编码字符串,需要借助querystring解析npm install querystring -D
命令下载import qs from 'querystring'
console.log('search参数', qs.parse(this.props.location.search.split('?')[1]) ) // search参数 {name: '111', age: '18'}
<Link to={{pathname:url, state:{参数名:参数值}}}></Link>
this.props.location.state // Object
<Link to={{pathname:'/home/sun1', state:{name:'111', age:18}}}>sun1</Link>
在子组件接收console.log('state', this.props.location.state ) // state {name: '111', age: 18}
<Link to=‘url/参数值/参数值’></Link>
<Route path="/demo/test/:参数名/:参数值" component={Test}/>
this.props.match.params // Object
<Link to='/home/sun1/111/18'>sun1</Link>
<Route path='/home/sun1/:name/:age' component={Sun1}></Route>
在子组件接收console.log('state', this.props.match.params ) // params {name: '111', age: '18'}
// search参数
this.props.history.push('url?参数名=参数值')
// state参数
this.props.history.push(url,{参数名,参数值})
// params参数
this.props.history.push('/home/message/detail/参数值')
现在有以下路由组件
import About from '../pages/About'
import Home from '../pages/Home'
import Sun1 from '../pages/Home/New'
import Sun2 from '../pages/Home/New2'
const routerList = [
{
id:1,
path:'/about',
component:About
},
{
id:2,
path:'/home',
component:Home,
routes:[
{
id:21,
path:'/home/sun1',
component:Sun1
},
{
id:22,
path:'/home/sun2',
component:Sun2
},
]
},
{
id:3,
path:'/home'
}
]
export default routerList
import React from 'react';
import { Switch , Route, Redirect} from 'react-router-dom';
// 组件数据通过props传入
export default function Router(props){
const { routes } = props
return (
<Switch>
{
routes.map(route=>{
if(route.component){
{/* 渲染每一个组件,若是存在子组件,将子组件数据传递到组件中(routes) */}
return <Route path={route.path} render={()=> <route.component routes={route.routes} key={route.id} />} ></Route>
}else{
return <Redirect to={route.path} key={route.id}/>
}
})
}
</Switch>
)
}
import Router from './router'
import routerList from './router/routerList';
export default class App extends Component {
render(){
return (
<div>
{/*
*/}
<Router routes={routerList}/>
</div>
)
}
import Router from '../../router'
export default class Home extends Component {
render() {
const { routes } = this.props
return (
<div>
我是home组件
{/*
*/}
<Router routes={ routes }></Router>
</div>
)
}
}
上述的语法以及案例都是使用的5.2.0版本的,现在我进行了以下操作
现在项目中使用的react-router-dom版本为 6.6.1
然后直接运行,会有一些语法问题 如下:
在6版本之后将Switch组件移除
,新添加了Routes
组件
语法
<Routes>
// 组件注册
</Routes>
两者的异同点为
在6版本之后将Redirect组件移除
,新添加了Navigate
组件
语法
<Navigate to='修改的路径' replace='是否使用push'></Navigate>
只要该组件被渲染,就会引起路径修改,切换视图(类似于编程式导航)
isActive
)作为参数传入,我们可以根据次做判断editclass = (data) =>{
const { isActive } = data
return isActive ? 'active' : ''
}
<NavLink className={this.editclass} to='/home' end>Home</NavLink>
useRout的作用是用于注册路由的(类似于我们之前自己封装的路由配置)
需要注意的是 hook 不能在类中使用,若是组件使用类写的,需要转换为function组件。
现在对上面 路由配置封装 环节的代码进行改造
import About from '../pages/About'
import Home from '../pages/Home'
import Sun1 from '../pages/Home/New'
import Sun2 from '../pages/Home/New2'
import { Navigate } from 'react-router-dom'
const routerList = [
{
id:1,
path:'/about',
element:<About />
},
{
id:2,
path:'/home',
element:<Home />,
children:[
{
id:21,
path:'/home/sun1',
element:<Sun1 />
},
{
id:22,
path:'/home/sun2',
element:<Sun2 />
},
]
},
{
id:3,
path:'/',
element: <Navigate to='/home' />
}
]
export default routerList
在6版本中 Route组件的component属性被element
属性所替代;import {Link,NavLink, useRoutes} from 'react-router-dom'
import './App.css';
import routerList from './router/routerList';
export default function App (){
const routes = useRoutes(routerList)
return (
<div>
<div className='left'>
<Link to='/about'>About</Link>
<br />
<NavLink to='/home' end>Home</NavLink>
<br />
<Link to='/home/sun1'>sun1</Link>
<br />
<Link to='/home/sun2'>sun2</Link>
</div>
<div className='right'>
{/*
*/}
{routes}
</div>
</div>
)
}
在根组件中将通过 useRoutes 得到的注册路由 放在 路由对应的位置,使得页面中数据可以正常显示,但是若是存在嵌套路由应该如何处理呢?Outlet
组件若是存在嵌套路由,需要借助 Outlet
组件
Outlet 组件相当于Vue中的路由出口 ,放在哪里当组件显示时就会将组件显示在对应的位置
import React, { Component } from 'react'
import {Outlet} from 'react-router-dom'
export default class Home extends Component {
render() {
return (
<div>
我是home组件
{/*
*/}
<Outlet />
</div>
)
}
}
若是使用 hook(useRoute) 进行路由注册,则路由跳转的语法也会对应发生变化
传递参数和之前一样
<Link to='/home/sun1/参数值/参数值'>sun1</Link>
{
id:21,
path:'/home/sun1/:属性名/:属性名',
element:<Sun1 />
},
<Link to='/home/sun1/111/18'>sun1</Link>
{
id:21,
path:'/home/sun1/:name/:age',
element:<Sun1 />
},
接收参数需要借助 hook useParams
import { useParams } from 'react-router-dom'
const params = useParams() //{name: '111', age: '18'}
useParams会解析params参数
<Link to='/home/sun1?name=111&age=18'>sun1</Link>
useSearchParams
const [searchParams, setSearch] = useSearchParams()
console.log('res', searchParams.get('name'), searchParams.get('age')) // 111 18
useSearchParams的返回值为一个数组,第一个元素为一个对象,该对象不可直接获取属性,需要通过get方法获取参数// 之前
<Link to={{pathname:'/home/sun1', state:{name:'111', age:'18'}}}>sun1</Link>
// 现在
<Link to='/home/sun1' state={{name:'111', age:'18'}}>sun1</Link>
useLocation
const {state} = useLocation()
console.log('res',state) // {name: '111', age: '18'}
通过以上方式配置的路由,在初始化时会将所有的路由组件同时加载,增加首评的加载速度。
使用懒加载之后就会在跳转到该路由时才会加载该路由
语法
import {lazy} from 'react'
lazy为一个函数,参数为一个回调函数,当跳转到该组件路径时,自自动调用该回调函数
举例说明
// 普通路由
// import About from '../pages/About'
// import Home from '../pages/Home'
// import StateDemo from '../pages/useSetstate'
// 路由懒加载
import {lazy} from 'react'
import { Navigate } from 'react-router-dom'
const Home = lazy(()=> import('../pages/Home'))
const About = lazy(()=> import('../pages/About'))
const StateDemo = lazy(()=>import('../pages/useSetstate'))
const routerList = [
{
id:1,
path:'/about',
element:<About />
},
{
id:2,
path:'/home',
element:<Home />,
},
{
id:4,
path:'/demo1',
element:<StateDemo />
},
{
id:3,
path:'/',
element: <Navigate to='/home' />
}
]
export default routerList
报错->此时会报错
原因
因为只有跳转到对应路径才会加载对应的组件,这个加载得时间中我们需要展示一个对应的组件,此时可以使用Suspense
这个组件
import React,{Suspense} from 'react';
root.render(
<BrowserRouter>
<React.StrictMode>
<Provider store={store}>
<Suspense fallback={<h1>loading......</h1>}>
<App/>
</Suspense>
</Provider>
</React.StrictMode>
</BrowserRouter>
);
fallback属性值可以是一个组件,也可以是一个虚拟dom
<Suspense fallback={<h1>loading......</h1>}>
<App/>
</Suspense>
<Suspense fallback={<Loading />}>
<App/>
</Suspense>