react router 6,嵌套路由、重定向、路由传参、编程式导航
-
react-router官网:https://reactrouter.com/
2021年11月 react router 6 成为默认版本,npm安装时自动安装6版本
每次react router发布都会有3个版本
react-router : 路由的核心库,提供了很多组件钩子
react-router-dom: 包含react-douter所有内容,并添加了一些专门用于DOM的组件,例如BrowserRouter
react-router-native: 包含react-douter所有内容,并添加了一些专门用于ReactNative的一些api,例如Nativerouter
react router 6版本与5版本有哪些改动?
1、内置组件的变化:移除<Switch> 新增Routers等
2、语法的变化:component={About} 变成 element={<About />}
3、新增多个hook:useParams、useNavigate、useMatch
4、官方声明推荐函数式组件
一、路由模式选者
首先在入口文件index.js用BrowserRouter把App组件包裹住,代表用的是BrowserRouter,还有一种模式是hashRouter
index.js
import React from 'react' import ReactDOM from 'react-dom/client' import { BrowserRouter } from 'react-router-dom' import App from './App' const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <BrowserRouter> <App/> </BrowserRouter> )
二、一级路由 ----- 路由的基本使用 NavLink 、 Link、Routes、 Route:
Route必须由Routes包裹住,不包裹会报错,原来的Switch包裹是为了解决一直向下匹配的问题,用Routes包裹后也不存在这个问题
另外,Route的component={About}改成了 element={<About/>}
App.jsx
import React from 'react' import { NavLink, Route, Routes } from 'react-router-dom' import Home from './pages/Home' import About from './pages/About' import Demo from './pages/Demo' import './App.css' export default function App() { return ( <div className="container"> <h1>react-router-dom-demo</h1> <div className="main"> <aside className="aside"> { /** * 编写路由链接 * 在react中,靠路由连接切换组件 */} <NavLink className="btn" to="/home">home</NavLink> <NavLink className="btn" to="/about">about</NavLink> </aside> <div className="content"> {/** * 注册路由 */} <Routes> {/* 路由匹配住/home后,不会再往下匹配 */} <Route path="/home" element={<Home/>}></Route> <Route path="/home" element={<Demo/>}></Route> <Route path="/about" element={<About/>}></Route> </Routes> </div> </div> </div> ) }
三、路由重定向 Navigate
当没有匹配任何路由的情况下,会有警告:No routes matched location "/"
5版本的重定向是使用Redirect: <Redirect to="/home"></Redirect>
6版本用Navigate组件,他有以下特性:
1、这个组件只要被渲染就会更改路径,切换路由
2、有一个replace属性,默认为false,为push模式,如果为true,就是replace模式(不会留下历史记录)
使用示例:
路由 / ,默认跳转到 /home路径 <Routes> <Route path="/home" element={<Home/>}></Route> <Route path="/about" element={<About/>}></Route> <Route path="/" element={<Navigate to="/home"/>}></Route> </Routes> 以下是通过条件判断,让路由切换 { sum === 2 ? <Navigate to="/about"></Navigate> : <div>当前sum的值是: {sum}</div>}
四、Routes与Route
1、v6版本移除了<Switch>,用Routes替代
2、Routes和Route要配合使用,必须用Routes包裹Route
3、Route相当于if语句,如果与当前URL匹配,就呈现对应组件
4、<Route caseSensitive /> 用于指定:匹配时是否区分大小写,默认为false
5、当URL发生变化时,<Routes>都会查其所有子<Route>以找到最佳匹配并呈现其页面
6、<Route/>也可以嵌套使用,且可配合useRoutes()配置'路由表',但需要通过 <outlet>组件来渲染子路由
五、NavLink高亮效果
5版本的NavLink高亮效果是组件内部会自动加上active类名,如果自定义高亮样式,可以加activeClassName,指定被选中的样式
6版本不支持activeClassName属性,router6要求,如果想自定义class类名,需要把className写成一个函数,返回类名
{ /** * 编写路由链接 * 在react中,靠路由连接切换组件 */} <NavLink className={ ({isActive}) => isActive ? 'btn route_active' : 'btn' } to="/home">home</NavLink> <NavLink className="btn" to="/about">about</NavLink>
嫌写的复杂了,可以提取出一个计算属性
function computedClassName({isActive}) { return isActive ? 'btn route_active' : 'btn' } <NavLink className={ computedClassName } to="/home">home</NavLink> <NavLink className={ computedClassName } to="/about">about</NavLink>
六、路由表的使用---useRoutes
我们把下面这个写成一个路由表,路由表必须是个数组
===》
import { NavLink, Route, Routes, Navigate, useRoutes } from 'react-router-dom' const element = useRoutes([ { path: '/home', element: <Home/> }, { path: '/about', element: <About/> }, { path: '/', element: <Navigate to="/home"/> } ]) {/** * 注册路由 */} {/* <Routes> <Route path="/home" element={<Home/>}></Route> <Route path="/ab0ut" element={<About/>}></Route> <Route path="/" element={<Navigate to="/home"/>}></Route> </Routes> */} 取而代之 {element}
一般路由表会专门用一个文件来写
routes/index.js
import { Navigate } from 'react-router-dom' import Home from '../pages/Home' import About from '../pages/About' const routes = [ { path: '/home', element: <Home/> }, { path: '/about', element: <About/> }, { path: '/', element: <Navigate to="/home"/> } ] export default routes
页面中引入使用:
import { NavLink, useRoutes } from 'react-router-dom' import routes from './routes' // 根据路由表生成对应的路由规则 const element = useRoutes(routes);
七、嵌套路由
router6的路由表统一在routes文件下配置,那么子路由就需要一个槽位来指定子路由显示的位置,这个标签是outlet,相当于vue中的router-view,
配置嵌套路由表:
routes/index.js
import { Navigate } from 'react-router-dom' import Home from '../pages/Home' import About from '../pages/About' import News from '../pages/News' import Message from '../pages/Message' const routes = [ { path: '/home', element: <Home/>, children: [ { path: 'news', element: <News/> }, { path: '/home/message', element: <Message/> }, ] }, { path: '/about', element: <About/> }, { path: '/', element: <Navigate to="/home"/> } ] export default routes
用Outlet指定子路由在页面中显示的位置:
import { Outlet } from 'react-router-dom'; {<Outlet/>}
另外一个小知识点,当切换到子路由时,对应的父级路由也会高亮,在父级路由的NavLink上加上end属性,可以不让其高亮
<NavLink className={ computedClassName } end to="/home">home</NavLink>
八、路由传参
(1)、params传参:接收参数时使用useParams函数
路由表里定义params参数
{ path: '/home/message', element: <Message/>, children: [ { path: 'detail/:id/:title/:content', element: <Detail/> } ] }
路由跳转时传入参数
messages.map(m => { return <li key={m.id}> <Link to={`detail/${m.id}/${m.title}/${m.content}`}>{m.title}</Link> </li> })
接收路由params参数,需要用到useParams()
import { useParams } from 'react-router-dom'
const params = useParams();
也可以用useMatch()
import { useMatch } from 'react-router-dom'
const a = useMatch('/home/message/detail/:id/:title/:content');
(2)、search传参
需要用useSearchParams()接收参数
search传参:
messages.map(m => { return <li key={m.id}> <Link to={`detail?id=${m.id}&title=${m.title}&content=${m.content}`}>{m.title}</Link> </li> })
接收参数:useSearchParams()
import React from 'react' import { useParams, useMatch, useSearchParams } from 'react-router-dom' export default function Detail() { const [search, setSearch] = useSearchParams(); const id = search.get('id'); const title = search.get('title'); const content = search.get('content'); return ( <ul> <button onClick={ () => setSearch('id=008&title=嘻嘻&content=哈哈') }>点我更改search参数</button> <li>id:{id}</li> <li>title:{title}</li> <li>content:{content}</li> </ul> ) }
也可以用useLocation()接收
import { useLocation } from 'react-router-dom' const x = useLocation();
(3)、state传参:
传参:
messages.map(m => { return <li key={m.id}> <Link to="detail" state={{ id: m.id, title: m.title, content: m.content }}>{m.title}</Link> </li> })
接收参数,使用useLocation()
import { useLocation } from 'react-router-dom'
const {state} = useLocation();
九、编程式导航:
需要用到useNavigate()
在router5中,普通组件想使用路由里的location、match、history,需要用withRouter(组件)加工一下,但router6不需要,可以直接用钩子函数就可以使用router的任何东西
const navigate = useNavigate();
路由跳转、传参
// search、params传参直接在路径中传参,state在第二个参数里传 navigate('detail',{ replace: false, state: { id:m.id, title:m.title, content:m.content, } })
前进、后退
navigate(1) // 前进 navigate(-1) // 后退
十、useInRouterContext()
作用:如果组件在<Router>的上下文中呈现,则useInRouterContext()返回true,否则返回false
被BrowserRouter或HashRouter包裹住的就是在路由环境中
import { useInRouterContext } from 'react-router-dom'
console.log(useInRouterContext());
十一、useNavigationType()
作用:返回当前导航类型(用户是如何来到当前页面的)
返回值:POP、PUSH、REPLACE
备注:POP是指在浏览器中直接打开这个路由组件(刷新页面)
import { useNavigationType } from 'react-router-dom'
const type = useNavigationType();
十二、useOutlet
作用:用来呈现当前组件中渲染的嵌套路由
import { useOutlet } from 'react-router-dom' const childRoute = useOutlet(); // 如果嵌套路由没有挂载,childRoute返回null // 如果嵌套路由已挂载,返回渲染的路由对象
十三、useResolvedPath()
作用:给一个URL值,解析期中的path、search、hash
import { useResolvedPath } from 'react-router-dom'
useResolvedPath('/user?id=001&name=tom#qwe')
-