前言
代码来源B站尚硅谷教学视频,可结合之前的路由文章【react框架】学习记录9-学习路由react-router-dom的使用
React Router 以三个不同的包发布到 npm 上,它们分别为:
react-router:路由的核心库,提供了很多的:组件、钩子。
react-router-dom:包含react-router所有内容,并添加一些专门用于 DOM 的组件,例如
<BrowserRouter>
等 。react-router-native:包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:
<NativeRouter>
等。--尚硅谷md文档
安装
yarn add react-router-dom
默认安装的是6版本
选择路由模式
history
...
import {BrowserRouter} from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
)
hash
用法一样,只是把标签改为HashRouter
LInk/routes/route
这几个放在一起讲。routes代替了原先的switch,并且是必写的,不写react会报错。
import {NavLink,Routes,Route} from 'react-router-dom'
<Link className="item" to="/about">About</NavLink>
<Link className="item" to="/home">Home</NavLink>
<Routes>
<Route path="/about" element={<About/>}/> // 注意哦,组件注册的写法变了
<Route path="/home" element={<Home/>}/>
</Routes>
其中Route标签支持一个属性caseSensitive,为true的时候,路由的路径就开启严格匹配模式,默认是false,路径不区分大小写。
6版中不需要使用专门使用Route去匹配路由了
Navigate
代替了Redirect标签
import React,{useState} from 'react'
import {Navigate} from 'react-router-dom'
export default function Home() {
const [sum,setSum] = useState(1)
return (
<div>
<h3>我是Home的内容</h3>
{/* 根据sum的值决定是否切换视图 */}
{sum === 1 ? <h4>sum的值为{sum}</h4> : <Navigate to="/about" replace={true}/>}
<button onClick={()=>setSum(2)}>点我将sum变为2</button>
</div>
)
}
其中有个replace属性,当为true的时候,push模式变成replace模式。
注意:当渲染Navigate标签的时候,如果Navigate标签不在routes的话,是真实的跳转到对应的组件页面,而不是像子组件一样渲染。
Navlink
舍弃了activeClassName属性,统一用className完成激活样式。可以写入一个函数,默认的入参是一个对象,里面有个标识是否激活的属性isActive:
<NavLink
to="login"
className={({ isActive }) => {
console.log('home', isActive)
return isActive ? 'base one' : 'base'
}}
>login</NavLink>
如果有多个NavLink标签可以写成一个方法直接在className中执行达到复用的效果。
后面会提到嵌套路由,当子路由的NavLink标签高亮的时候,同时父路由的NavLink也是高亮的,如果业务需求不需要父路由的NavLink高亮,可以在父级的NavLink标签上加end属性取消子路由匹配后的高亮。
<NavLink to="home" end >home</NavLink>
useRoutes使用路由表
就是和vue-router的路由统一管理一样的。
可以在src/routes/index.js中创建路由表配置
import About from '../pages/About'
import Home from '../pages/Home'
import {Navigate} from 'react-router-dom'
export default [
{
path:'/about',
element:<About/>
},
{
path:'/home',
element:<Home/>
},
{
path:'/',
element:<Navigate to="/about"/>
}
]
然后在需要显示路由组件的组件中:
import React from 'react'
import {NavLink,useRoutes} from 'react-router-dom'
import routes from './routes'
export default function App() {
//根据路由表生成对应的路由规则
const element = useRoutes(routes)
return (
<div>
......
{/* 注册路由 */}
{element}
......
</div>
)
}
嵌套路由
首先在路由表中写入子路由配置
const element = useRoutes([
{
path:'/about',
element:<About/>
},
{
path:'/home',
element:<Home/>,
children:[
{
path:'news',
element:<News/>
},
{
path:'message',
element:<Message/>,
}
]
}
])
然后利用Outlet标签做显示,原理和vue的router-view标签一样的:
import React from 'react'
import {NavLink,Outlet} from 'react-router-dom'
export default function Home() {
return (
<div>
<h2>Home组件内容</h2>
<div>
<ul className="nav nav-tabs">
<li>
<NavLink className="list-group-item" to="news">News</NavLink>
</li>
<li>
<NavLink className="list-group-item" to="message">Message</NavLink>
</li>
</ul>
{/* 指定路由组件呈现的位置 */}
<Outlet />
</div>
</div>
)
}
注意二级路由的to属性不要加/符号了,否则又是跳转一级路由
hooks传参
在函数式组件中由于获取不到this,所以react提供了几个hooks用于路由的信息获取
useParams获取params参数
params参数的接收用useParams:
{/* 向路由组件传递params参数 */}
<Link to={`/home/message/detail/${ obj.id }/${ obj.title }`}>{ obj.title }</Link>
{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" element={ Zizujian }/> // 或者在路由表中配置
// 子组件接收参数
import { Routes, Route, useParams } from 'react-router-dom';
let { id } = useParams(); // 获取传参
useSearchParams获取search参数
search参数的接收用useSearchParams:
// 父组件
<Link to={`/home/message/detail/?id=${ obj.id }&title=${ obj.title }`}></Link>
// 子组件
import React from 'react'
import {useSearchParams} from 'react-router-dom'
export default function Detail() {
const [search,setSearch] = useSearchParams() // 和stateHook的用法一样
const id = search.get('id') // 只是获取到的search是个封装好的类型,需要get获取数据
const title = search.get('title')
const content = search.get('content')
return (
<ul>
<li>
<button onClick={()=>setSearch('id=008&title=哈哈&content=嘻嘻')}>点我更新一下收到的search参数</button> // 更新search参数
</li>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul>
)
}
useMatch获取match
获取路由匹配信息,之前router5的时候,我们获取params参数就是从这里拿取的:
import { useMatch } from 'react-router-dom';
const match = useMatch('/login/:x/:y') // 需要传入匹配的路由
console.log('params', match.params)
useLocation获取location
获取当前 location 信息,之前router5是通过location属性获取search参数:
import {useLocation} from 'react-router-dom'
const location = useLocation() // 不需要传入路由
console.log('search', location.search)
state的参数也是从这个api拿取的
// 父组件
<Link to='/home/message/detail', state:{ id: obj.id, title: obj.title }}}>{ obj.title }</Link>
// 子组件
import {useLocation} from 'react-router-dom'
const location = useLocation() // 不需要传入路由
console.log('state', location.state)
useNavigate实现编程式导航
import React from 'react'
import {useNavigate} from 'react-router-dom'
export default function Demo() {
const navigate = useNavigate()
const handle = () => {
//第一种使用方式:指定具体的路径
navigate('/login',
replace: false,
state: {a:1, b:2},
search: 'id=11' // 动态参数
})
//第二种使用方式:传入数值进行前进或后退,类似于5.x中的 history.go()方法
navigate(-1)
}
return (
<div>
<button onClick={handle}>按钮</button>
</div>
)
}
useInRouterContext判断是否被路由标签包裹
就是用来判断组件是否在<BrowserRouter>
标签的包括范围内,是返回 true,否则返回 false。
import {useInRouterContext} from 'react-router-dom'
console.log("@“, useInRouterContext())
useNavigationType获取路由跳转方式
import {useNavigationType} from 'react-router-dom'
console.log('useNavigationType', useNavigationType()) // PUSH REPLACE POP(原地刷新)
useOutlet获取当前组件中渲染的嵌套路由实例
const result = useOutlet()
console.log(result)
// 如果嵌套路由没有挂载,则result为null
// 如果嵌套路由已经挂载,则展示嵌套的路由对象
useResolvedPath解析rul
console.log(useResolvedPath('/home?id=1&name=22')) // 解析出对象的形式,有path、search、hash等
useRouteLoaderData路由表传hook
没想到吧,路由表还可以传参数,而且通过hook的方式传的。
先写个要传的hook:
import api from '@/api'
export default async function AuthLoader() {
const data = await api.getPermissionList()
return {
data
}
}
然后路由表配置好这个hook
// 引入
{
id: 'layout',
element: <Layout />,
loader: AuthLoader,
...
在Layout组件中使用(好像在它的子组件里也是可以用的,在其他组件里没试过)
import { useRouteLoaderData } from 'react-router-dom'
// ...
const data = useRouteLoaderData('layout')
结合TS
给个写法参考吧,不保证时效性,在router/index.tsx文件(注意后缀!!):
import { createBrowserRouter } from 'react-router-dom'
import React from 'react' // 必须引入否则报错
import Home from '../pages/Home'
import NotFound from '../pages/NotFound'
const router = createBrowserRouter([
{
path: '/',
element: <Home />,
},
{
path: '*', // 404 路由配置,都写在最后(兜底)
element: <NotFound />,
},
])
export default router
在App.tsx:
import React from 'react'
import { RouterProvider } from 'react-router-dom'
import routerConfig from './router'
import './App.css'
function App() {
return <RouterProvider router={routerConfig}></RouterProvider>
}
export default App