React路由
react-router-dom@6
⚙️安装
npm i react-router-dom
🔧 使用
另一种routes写法,见
//> App.js
import { HashRouter, Routes, Route } from 'react-router-dom'
// 导入路由组件
import Film from 'xxx.js'
import Cinema from 'yyy.js'
//....
export default function App(){
return(
<HashRouter>
<Routes>
{/* 匹配路径,切换路由组件 */}
<Route path'/film' element={<Film />} />
<Route path='/cinema' element={<Cinema />} />
<Route path='/login' element={<Login />} />
</Routes>
</HashRouter>
)
}
常规写法将路由配置提出,到/src/router/router.js
//> src/router/router.js
import { Routes, Route } from 'react-router-dom'
// 导入路由组件
import Film from 'xxx.js'
import Cinema from 'yyy.js'
//....
export default MyRouter function(){
return(
<Routes>
{/* 匹配路径,切换路由组件 */}
<Route path'/film' element={<Film />} />
<Route path='/cinema' element={<Cinema />} />
<Route path='/login' element={<Login />} />
</Routes>
)
}
//> App.js
import { HashRouter } from 'react-router-dom'
import MyRouter from '/router/router.js'
export default function App(){
return(
<HashRouter>
{/* 渲染所需要的路由组件, 这里我们将路由组件提出 */}
</HashRouter>
)
}
*
通配符用于匹配所有路径。它可以在路由配置中的路径中使用,表示匹配任何路径。需要注意的是,通配符应该放在路由配置中的最后,以确保其他具体的路径能够优先匹配。
路由重定向Redirect
利用useNavigate
导航钩子函数,自定义重定向组件
//> /src/component/Redirect.js
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
// 利用props传值
const Redirect = ({ to }) => {
const Navigate = useNavigate()
useEffect(()=>{
navigate(to, { replace: true })
})
return null
}
export default Redirect
//> router.js
//import ...
import Redirect from '/component/Redirect.js'
export default MyRouter function(){
return(
<Routes>
{/* 重定向到now-playing路由界面 */}
<Route path'/film' element={ <Redirect to='/now-playing'/> } />
<Route path='/now-playing' element={<NowPlaying />} />
</Routes>
)
}
路由嵌套
//> router.js
//import ...
import Redirect from '/component/Redirect.js'
export default MyRouter function(){
return(
<Routes>
<Route path'/film' element={ <Film /> }>
{/*index 匹配父级根路径 */}
<Route index element={<Redirect to='/film/comming-soon'/>
{/*直接写这一级路由名字,不必加上父路径 */}
<Route path='now-playing' />
</Route>
<Route path='/now-playing' element={<NowPlaying />} />
</Routes>
)
}
在父路由组件中需要占位Outlet
(类似Vue的router-view)
//> /src/views/Film.js
export default function Film (){
return (
<div>
{/* 任意层级。。。 */}
<Outlet></Outlet>
</div>
)
}
🧭导航
声明式导航
Link
import { Link } from 'react-router-dom'
//....
// to路由路径,编译成<a ..></a>
<Link to='/film'>电影</Link>
<Link to='/cinema'>电影院</Link>
<Link to='/center'>个人中心</Link>
NavLink
(可以给点击的链接设置高亮)
NavLink
组件的className属性传递了一个函数,函数参数接受了一个对象,结构出来有两个属性,其中一个为isActive
如果当前路由与 NavLink
的 to
属性匹配,则 isActive
为 true
,对应的类名为 active
,否则为 inactive
。
另一个解构出来的参数为
isPending
:表示当前路由是否正在进行匹配过程中。如果正在匹配过程中,isPending
为true
,否则为false
。
import { NavLink } from 'react-router-dom'
// 设置高亮的css文件
import from 'highlight.css'
//....
<NavLink className={({isActive})=>isActive ? 'highlight-active' : ''} to='/film'>电影</NavLink>
<NavLink className={({isActive})=>isActive ? 'highlight-active' : ''} to='/cinema'>电影院</NavLink>
<NavLink className={({isActive})=>isActive ? 'highlight-active' : ''} to='/center'>个人中心</NavLink>
编程式导航
this.props.history.push() v5版本 //class类组件
利用useNavigate
钩子函数,进行导航路由跳转
import { useNavigate } from 'react-router-dom'
export default function xxx (){
const navigate = useNavigate()
return(
<div>
<button onClick={()=>{
{/*跳转到login页面*/}
navigate('/login')
}}>点击跳转</button>
</div>
)
}
跳转路由传参
query传参,配合useSearchParams
import { useNavigate } from 'react-router-dom'
export default function FilmItem(){
const navigate = useNavigate()
const myid = 'test-id'
return(
<div>
<button onlClick={()=>{
navigate(`/detail?id=${myid}`)
}}>点击</button>
</div>
)
}
import { useSearchParams } from 'react-router-dom'
export default function FilmItemDetail(){
// params 当前页面的query参数, setparams设置当前页面的参数
const [params, setparams] = useSearchParams()
return(
<div>
<button onClick={()=>{
console.log(params.get('id')) {/*获取当前query参数myid */}
console.log(params.has('id')) {/*判断是存在 */}
setparms({id: '19wjqdnubwhqi1'}) {/*改变当前页面query参数, 只是更新当前页面参数,需要配合navigate跳转 */}
}}>点击</button>
</div>
)
}
路由传参,占位,配合useParams
,useNavigate
路由跳转
//> router.js
···
<Route path='/detail' element={<Detail/ >}>
<Route path='detail/:myid'element={<Detail/ >}> //占位
···
//> Detail.js
import { useParams } from 'react-router-dom'
export default function FilmItemDetail(){
// params 当前页面的路由参数
const params = useParams()
const navigate = useNavigate()
return(
<div>
<button onClick={()=>{
navigate(`/detail/1000`)
}}>点击</button>
</div>
)
}
🛑路由拦截
权限验证:
//> router.js
//验证模块
const isAuth = () => {
return localStorage.getItem('token')
}
<Route path='/center' element={isAuth() ? <Center /> : <Redirect to='/login' />} />
⚠️ 有bug,这个只会执行一次
v5 是利用 render函数重新渲染解决
render = {()=>isAuth() ? <Center /> : <Redirect to='/login' />}
🔰Final 利用插槽的形式,传入需要验证的组件
// >router.js
// 用于权限验证的组件
function AuthComponent({ children }){
const isLogin = localStorage.getItem('token')
// 有Token:渲染插槽组件,没有:重定向到登录页
return isLogin ? children : <Redirect to='/login' />
}
<Route path='/center' element={<AuthComponent>
<Center />
</AuthComponent>} />
路由模式
HashRouter ,打包后,放在后端静态资源目录下就能使用(监测url哈希值改变)
BrowserRouter ,需要服务器端的配置来确保在刷新页面或直接访问某个路由时,服务器能够正确地响应并返回应用的入口 HTML 文件。(浏览器history API)
Web服务器按路由寻址访问
🟢 withRouter /类组件跳转方法
hooks中,在任何由Router包裹的组件都可使用useNavigate()钩子
高阶组件,是一种用于增强或修改组件行为的设计模式。
它本质上是一个函数,接受一个组件作为参数,并return 一个新的增强后的组件
v5类组件
this.props.history.push() 跳转页面
this.props.history.match() 获取路由参数
this.props.history.location() 获取当前路由
withRouter就是一个高阶函数, 用于将 React Router 的路由信息(如 match
、location
和 history
)作为 props 传递给包裹的组件。它用于在不是通过路由组件直接渲染的组件中访问路由信息。
案例:
在v5组件升级到v6版本时
想要进行路由导航,但是在类组件中不能直接使用useNavigate
方式
而此时v6版本类组件组件又没有history相关方法
所以采用折中的方法,自定义withRouter组件,将v6的一些钩子函数注入到需要的类组件中
//> withRouter.js
import React from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
export default function withRouter(Component) {
return function (props) {
const push = useNavigate()
const match = useParams()
const location = useLocation()
{/* 注入新的props: history */}
return <Component {...props} history={{ push, match, location }}/>
}
}
//使用withRouter的类组件
import React, { Component } from 'react'
import withRouter from '../../component/withRouter'
class FilmItem extends Component {
handleChangePage = (id) => {
this.props.history.push(`/detail/${id}`) {/**/}
}
render() {
console.log('-', this.props)
return (
<li onClick={() => this.handleChangePage(this.props.filmId)}>
{this.props.name}
</li>
)
}
}
{/* 调用withRouter,以增强当前类组件,实现使用路由方法 */}
export default withRouter(FilmItem)
路由懒加载
路由按需加载
利用React.lazy + React.Suspense
//封装一个懒加载模块
//根据path路径加载
const lazyLoad = (path) => {
const Component = React.lazy(()=>import(`../views/${path}`))
return (
<React.Suspense fallback={<span>加载中...</span>}>
<Component />
</React.Suspense>
)
}
使用
//> router.js
//注意path是传入的路径, 因为刚刚的模版字符串,当前的path位文件名
<Route path='/cinema' element={lazyLoad('Cinema')}></Route>
useRoutes
类似Vue路由的配置写法
export default function MyRouter() {
const element = useRoutes([
{
path: '/film',
element: lazyLoad('Film'),
children: [
{
path: 'now-playing',
element: <NowPlaying />
},
{
path: 'comming-soon',
element: <CommingSoon />
}
]
},
{
path: '/cinema',
element: lazyLoad('Cinema')
},
{
path: '/center',
element: <AuthComponent>
{
lazyLoad('Center')
}
</AuthComponent>
}
])
return element
}