react-router-dom v6 整体体验相对于 v5 ,体验要好更多,最⼤的⼀个改变,就是曾经的 Route 不可嵌套,整个路由配置必须拆分 成若⼲⼩块,除⾮通过 react-router-config 这种插件,才可以实现对整个路由的管理,然⽽现在,不需要任何插件就可实现对路由配置 的管理。
npm i react-router-dom -S
import React from 'react
//引入路由中的各种API
import {
HashRouter,
BrowserRouter,
Routes,
Route,
Navigate,
Link,
NavLink,
Outlet
} from 'react-router-dom'
// 引入路由相关组件
import Home from './Home'
import Cart from './Cart'
import My from './My'
import NotFound from './NotFound'
import FooterNav from './FooterNav'
import Tv from './home/Tv'
import Ai from './home/Ai'
import Login from './Login'
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/home" element={<Home></Home>}>
<Route path="tv" element={<Tv></Tv>}></Route>
<Route path="ai" element={<Ai></Ai>}></Route>
</Route>
<Route path="/cart" element={<Cart></Cart>}></Route>
<Route path="/login" element={<Login></Login>}></Route>
<Route path="/my" element={<My></My>}></Route>
<Route path="/" element={<Navigate to="/home"></Navigate>}></Route>
<Route path="*" element={<NotFound></NotFound>}></Route>
</Routes>
<FooterNav></FooterNav>
</BrowserRouter>
)
}
Routes 组件替换 v5 的 Switch 组件
路由配置的包裹元素,必须放在中,不然报错
配置路由, Route里面的path代表路径,element配置映射的路由组件
<Route path="/cart" element={<Cart></Cart>}></Route>
element的值是一个js表达式,意味着也可能根据需求写业务代码
//验证token
const isLogin = () => localStorage.getItem("token") ? true : false
//登录验证,验证成功进入Cart,验证失败进入登录页
<Route path="/cart" element={isLogin() ?<Cart></Cart>:<Login></Login>}></Route>
v6 移除了 Redirect 组件,改⽤ Navigate 组件。
代表重定向,to代表跳到目标路由地址
<Route path="/" element={<Navigate to="/home"></Navigate>}></Route>
注意: Navigate不能是的子元素
二者都是实现路由跳转的
不同之处是NavLink会给激活链接自动添加active的类名
<Link to="/movie" >电影</Link>
<NavLink to="/find" class="active">发现</NavLink>
App.js中配置的二级路由
<Route path="/home" element={<Home></Home>}>
<Route path="tv" element={<Tv></Tv>}></Route>
<Route path="ai" element={<Ai></Ai>}></Route>
</Route>
嵌套路由必须在⽗组件中追加 Outlet 组件,作为⼦级组件的占位符,类似于 vue-router 中的 router-view 。
在嵌套路由中,如果URL仅匹配了父级URL,则Outlet
中会显示带有index
属性的路由
<Route path="/home" element={<Home></Home>}>
<Route index element={<Tv></Tv>}></Route>
<Route path="ai" element={<Ai></Ai>}></Route>
</Route>
Home.js中设置子路由出口
import React,{useState,useEffect} from 'react'
import style from './Home.css'
import { Outlet,NavLink,useNavigate} from 'react-router-dom'
export default function Home() {
return (
<div>
<div className="header">
<p><NavLink to="" >电视</NavLink></p>
<p><NavLink to="ai" >智能</NavLink></p>
</div>
<Outlet></Outlet>
</div>
)
}
格式: /路径/:参数
<Route path="/list/:id" element={<List></List>}></Route>
参数id=5
列表
在rouer6中,函数组件无法用props中接收路由参数,只能使用useParams这个hook
import { useParams } from 'react-router-dom';
...
const params = useParams()
console.log(params.id) //5
只有函数组件才有Hook
import { useNavigate,useParams,useSearchParams,useLocation } from 'react-router-dom';
const navigate = useNavigate()
//跳转到/my
navigate("/my")
//跳转到上一页
navigate(-1)
//跳转到/my,并替换当前历史记录
navigate("/my",{replace: true})
//携带参数
navigate("/my",{replace: true,state:{arr:[1,2,3]}})
state在组件中通过useLocation().state来获取
动态路由
<Route path="/list/:id" element={<List></List>}></Route>
**路由跳转,参数id=5 **
<p><NavLink to="/list/5">列表</NavLink></p>
获取动态路由参数
const params = useParams()
console.log(params.id) //5
获取查询参数, 查询参数不需要在路由中定义
/list?type=cart
使用useSearchParams
hook来访问查询参数。其用法和useState类似,会返回当前对象和更改它的方法
更改searchParams时,必须传入所有的查询参数,否则会覆盖已有参数
import { useSearchParams } from 'react-router-dom';
// 当前路径为 /foo?id=12
function Foo(){
const [searchParams] = useSearchParams();
console.log(searchParams.get('id')) // 12
setSearchParams({
name: 'foo'
}) // /foo?name=foo
return (
<div>foo</div>
)
}
返回代表当前URL的location对象
import { useNavigate,useParams,useLocation } from 'react-router-dom';
...
export default function List() {
//用navigate实现编程式导航
const navigate = useNavigate()
//获取动态路由的参数
const params = useParams()
//获取url信息
const location = useLocation()
const toUrl = ()=>{
// navigate("/my")
navigate("/my",{replace: true})
}
return (
<div>
list
<hr />
<button onClick={toUrl}>回到我的</button>
</div>
)
}
.footerNav {
display: flex;
justify-content: space-around;
position: absolute;
width: 100%;
height: 60px;
left: 0;
bottom: 0;
background-color: #aaa;
}
.footerNav .active{
border: 1px solid #000;
background-color: #f00;
}
import React from 'react'
import { Link, NavLink } from 'react-router-dom'
import './FooterNav.css'
export default function FooterNav() {
return (
<div className="footerNav">
<p><NavLink to="/home">首页</NavLink></p>
<p><NavLink to="/cart">购物车</NavLink></p>
<p><NavLink to="/my">我的</NavLink></p>
<p><NavLink to="/list/5">列表</NavLink></p>
</div>
)
}
import React from 'react'
import { HashRouter,BrowserRouter,Routes, Route,Navigate,Link,NavLink,Outlet} from 'react-router-dom'
import Home from './Home'
import Cart from './Cart'
import My from './My'
import NotFound from './NotFound'
import FooterNav from './FooterNav'
import Tv from './home/Tv'
import Ai from './home/Ai'
import Login from './Login'
import isLogin from './util/token'
import List from './List'
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/home" element={<Home></Home>}>
<Route path="tv" element={<Tv></Tv>}></Route>
<Route path="ai" element={<Ai></Ai>}></Route>
</Route>
<Route path="/cart" element={isLogin() ?<Cart></Cart>:<Login></Login>}></Route>
<Route path="/login" element={<Login></Login>}></Route>
<Route path="/my" element={<My></My>}></Route>
<Route path="/my" element={<My></My>}></Route>
<Route path="/list" element={<List></List>}></Route>
<Route path="/" element={<Navigate to="/home"></Navigate>}></Route>
<Route path="*" element={<NotFound></NotFound>}></Route>
</Routes>
<FooterNav></FooterNav>
</BrowserRouter>
)
}
.header {
display: flex;
justify-content: space-around;
}
.header .active {
border: 1px solid #000;
}
.header .atdh {
background-color: #f00;
}
import React,{useState,useEffect} from 'react'
import style from './Home.css'
import { Outlet,NavLink,useNavigate} from 'react-router-dom'
export default function Home() {
const navigate = useNavigate();
useEffect(()=>{
navigate('/home/tv')
},[])// eslint-disable-line
return (
<div>
<div className="header">
<p><NavLink to="tv" >电视</NavLink></p>
<p><NavLink to="ai" >智能</NavLink></p>
</div>
<Outlet></Outlet>
</div>
)
}
上面通过把 二级路由Route 作为 一级路由Route 的 children,实现二级路由配置
<Route path="/home" element={<Home></Home>}>
<Route path="tv" element={<Tv></Tv>}></Route>
<Route path="ai" element={<Ai></Ai>}></Route>
</Route>
但是我们还可以通过useRoutes
生成对应的 element
useRoutes 可以将数组对象形式的路由,直接在页⾯上使⽤。
在App.js中通过json数据实现路由配置
import React from 'react'
import { BrowserRouter as Router, Navigate, useRoutes } from 'react-router-dom'
import Home from './pages/Home'
import Category from './pages/Category'
import Phone from './pages/Category/Phone'
import NoteBook from './pages/Category/NoteBook'
import GoodsList from './pages/GoodsList'
import My from './pages/My'
import Detail from './pages/Detail'
import NotFound from './pages/NotFound'
const GetAllRoutes = () => {
const routes = useRoutes([
{
path: '/',
element: <Navigate to="/Home" />
},
{
path: '/Home',
element: <Home />
},
{
path: '/Category',
element: <Category />,
children: [
{
//默认路由
index: true,
element: <Phone />
},
{
path: 'NoteBook',
element: <NoteBook />
}
]
},
{
path: '/GoodsList',
element: <GoodsList />
},
{
path: '/My',
element: <My />
},
{
path: '/Detail/:id',
element: <Detail />
},
{
path: '/404',
element: <NotFound />
},
{
path: '*',
element: <NotFound />
}
])
return routes;
}
export default function App() {
return (
<Router>
<GetAllRoutes />
</Router>
)
}
其它代码参考仿小米App的路由配置,只是与首页相关的,由/home改为/ , /home/tv, 改为/tv
一个页面中也许会有许多的逻辑代码并不需要在页面加载时加载,也许是在触发了某个事件如点击之后才需要加载。那么通过动态加载就可以实现这一点,好处在于可以加快页面加载的速度。
import {lasy} from 'react'
const Later = lazy(() => import('./Later'));
既然是动态加载,那么也就是说会有一个加载过程,那么在这个加载过程中就需要一个UI组件,来在等待过程中显示一些UI,Suspense组件实现这个功能
suspense
组件有一个fallback
属性,它就是用来接收加载过程中显示的组件的。suspense
内部是可以包裹多个组件的。
import { lazy, Suspense } from 'react';
const Later = lazy(() => import('./Later'));
export default function App {
return (
<div>
<Suspense fallback={<div>loading...</div>}>
<Later />
</Suspense>
</div>
);
}
App.js
src/routes/index.js
src/routes/config.js
src/routes/privateRoute.js
src/privateRoute.js
/**
* PrivateRoute使用方式
* } tag="权限">
* props: {element:, tag: }
*/
import { Navigate } from "react-router-dom";
const PrivateRoute = props => {
const isLogin = localStorage.getItem("token")
return isLogin ? (
(props.element)
) : (
<Navigate to="/login"></Navigate>
);
};
export default PrivateRoute;
src/routes/config.js
import PrivateRoute from './privateRoute';
import { Suspense } from 'react';
const WrapperRouteComponent = ({ titleId, auth, ...props }) => {
if (titleId) {
document.title = titleId
}
return (
<Suspense fallback={<div>loading...</div>}>
{auth ? <PrivateRoute {...props} /> : (props.element)}
</Suspense>
)
};
export default WrapperRouteComponent;
src/routes/index.js
import { lazy } from 'react';
import Home from '../Home';
import WrapperRouteComponent from './config';
import { useRoutes, Navigate } from 'react-router-dom';
const NotFound = lazy(() => import(/* webpackChunkName: "404'"*/ '../NotFound'));
const Ai = lazy(() => import(/* webpackChunkName: "ai'"*/ '../home/Ai'));
const Tv = lazy(() => import(/* webpackChunkName: "tv'"*/ '../home/Tv'));
const Cart = lazy(() => import(/* webpackChunkName: "Cart'"*/ '../Cart'));
const My = lazy(() => import(/* webpackChunkName: "My'"*/ '../My'));
const List = lazy(() => import(/* webpackChunkName: "List'"*/ '../List'));
const Detail = lazy(() => import(/* webpackChunkName: "Detail'"*/ '../Detail'));
const Login = lazy(() => import(/* webpackChunkName: "Login'"*/ '../Login'));
const routeList = [
{
path: '/',
element: <Navigate to="/home" />
},
{
path: '/home',
// element: ,
element: <WrapperRouteComponent element={<Home />} titleId="首页" />,
children: [
{
path: 'ai',
element: <WrapperRouteComponent element={<Ai />} titleId="ai" />,
},
{
path: 'tv',
element: <WrapperRouteComponent element={<Tv />} titleId="tv" />,
}
]
},
{
path: '/cart',
element: <WrapperRouteComponent element={<Cart />} titleId="Cart" auth={true}/>,
},
{
path: '/my',
element: <WrapperRouteComponent element={<My />} titleId="My" />
},
{
path: '/list',
element: <WrapperRouteComponent element={<List />} titleId="List" />
},
{
path: '/detail/:id/:type',
element: <WrapperRouteComponent element={<Detail />} titleId="Detail" />
},
{
path: '/login',
element: <WrapperRouteComponent element={<Login />} titleId="Login" />
},
{
path: '/404',
element: <NotFound />
},
{
path: '*',
element: <NotFound />
}
]
const RenderRouter = () => {
const element = useRoutes(routeList);
return element;
};
export default RenderRouter;
App.js
import React from 'react'
import "./App.css"
//引入路由中的各种API
import {
BrowserRouter as Router,
} from 'react-router-dom'
import RenderRouter from './routes'
import TabBar from './TabBar'
export default function App() {
return (
<div className="app">
<Router>
<RenderRouter></RenderRouter>
<TabBar></TabBar>
</Router>
</div>
)
}