router
react中使用的是 react-router-dom
这个包实现路由功能。
路由的本质就是一个 key value 的映射关系
key --> url
value --> react路由组件
作用:
当在访问url地址的时候,展示这个地址对应的react路由组件
前端路由模式,是解决SPA应用的手段
S single 单独
P page 页面
A application 应用
SPA- 单页面应用:只有一个 html的应用
基本概念
Routes组件
Routes组件:路由出口组件,包裹的路由配置将渲染此处
Route组件
Route组件:路由配置
属性:
path: 指定url
element: 指定 路由组件
示例:
<Route path='/login' element={<Login/>}></Route>
使用案例:
<Routes>
<Route path='/login' element={<Login/>}></Route>
<Route path='/home' element={<Home/>}></Route>
</Routes>
// localhost:3000/login ===> Login组件的内容
// localhost:3000/home ===> Home组件的内容
BrowserRouter组件
是一个容器组件,只有被该组件包裹的组件及其后代组件,才能进行路由切换
<BrowserRouter>
<App/>
</BrowserRouter>
基本使用
安装
npm i react-router-dom
导入容器组件:在入口文件导入:src->index.js
import {BrowserRouter} from 'react-router-dom
使用容器组件包裹根组件:在入口文件配置:src->index.js
<BrowserRouter>
<App/>
</BrowserRouter>
创建路由组件:路由组件的存放目录
src/pages or src/views : 路由组件
src/components: 非路由组件
普通组件和路由组件有什么区别?
路由组件有 key value 的配置
进行路由配置:在路由出口位置配置:src->App.jsx
import { Route, Routes } from 'react-router-dom'
<Routes>
<Route path='/login' element={<Login/>}></Route>
<Route path='/home' element={<Home/>}></Route>
</Routes>
//当对应的路由激活的时候,对应路由组件会替换掉 <Routes>组件所在位置
设置路由重定向
# 写法一:使用Navigate组件包裹
<Route path='/' element={<Navigate to='/home' />}></Route>
# 写法二:使用index={true} 代替 path='/'
<Route index={true} element={<Navigate to='/home' />}></Route>
# 写法三:写法二的一种省略方式
<Route index element={<Navigate to='/home' />}></Route>
设置路由404
# 没有url 匹配成功的时候,会匹配到 * 展示页面找不到的路由组件,写在最后
<Route path='*' element={<PageNotFound/>}></Route>
路由重定向
Navigate组件
通过使用 Navigate
组件设置路由重定向
{/* Navigate重定向 */}
{/* <Route path='/' element={<Navigate to='/home' />}></Route> */}
{/* <Route index={true} element={<Navigate to='/home' />}></Route> */}
<Route index element={<Navigate to='/home' />}></Route>
路由跳转
NavLink 和 Link组件都会渲染成 a标签,NavLink组件有默认高亮样式 active, Link没有
NavLink组件
NavLink组件实现路由跳转
import { Navigate, NavLink, Route, Routes } from 'react-router-dom'
<h4>NavLink 跳转标签组件 高亮样式 渲染后的a标签上class属性中有 active</h4>
<ul>
<li><NavLink to="/home">首页</NavLink></li>
<li><NavLink to="/login">登录页</NavLink></li>
<li><NavLink to="/user">用户中心</NavLink></li>
</ul>
{/* to='/home/news' 简写为 to='news' */}
<NavLink className="list-group-item" to="news">News</NavLink>
Link组件
Link组件实现路由跳转
<h4>Link 跳转组件,渲染后的a标签上没有class属性上没有active</h4>
<ul>
<li><Link to="/home">首页</Link></li>
<li><Link to="/login">登录页</Link></li>
<li><Link to="/user">用户中心</Link></li>
</ul>
NavLink组件和Link组件的区别
路由导航自定义类名
NavLink 自定义高亮样式类名
原理:
className 的值可以是一个回调函数,回调函数的参数是一个对象,对象中有一个 isActive的属性,值是布尔值,标识这当前路由是否是激活的。
回调函数的返回值,会成为 className的样式类名
查看这个对象
<ul>
<li><NavLink to="/home" className={(obj) => { console.log(obj)}}>首页</NavLink></li>
<li><NavLink to="/login">登录页</NavLink></li>
<li><NavLink to="/user">用户中心</NavLink></li>
</ul>
// 非激活状态
{isActive: false, isPending: false, isTransitioning: false}
// 激活状态
{isActive: true, isPending: false, isTransitioning: false}
使用案例
<h4>NavLink 自定义高亮样式类名</h4>
<ul>
<li><NavLink to="/home" className={({isActive}) => {
return isActive ? 'myselfActive' : ''
}}>首页</NavLink></li>
<li><NavLink to="/login" className={({isActive}) => {
return isActive ? 'myselfActive' : ''
}}>登录页</NavLink></li>
<li><NavLink to="/user" className={({isActive}) => {
return isActive ? 'myselfActive' : ''
}}>用户中心</NavLink></li>
</ul>
路由表
定义路由配置对象:src->routes->index.js
import { Navigate} from 'react-router-dom'
import Home from '../pages/Home'
import Login from '../pages/Login'
import User from '../pages/User'
import NotFound from '../pages/NotFound'
const routes = [
{
path: '/login',
element: <Login />
},
{
path: '/home',
element: <Home />
},
{
path: '/user',
element: <User />
},
// 重定向
{
index: true, // path:'/'
element: <Navigate to='/home' />
},
// 404配置
{
path: '*',
element: <NotFound />
}
]
export default routes
useRouters函数
使用 `useRoutes` Hook函数根据路由表生成路由配置
useRoutes(参数) 参数是一个数组对象
useRoutes返回值是一个可用来渲染路由树的有效 React 元素,如果没有匹配项则返回 null
使用案例
在主组件中导入路由对象生成路由映射关系: src->App.js
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'
export default function App() {
return (
<div>
<h4>App组件</h4>
<hr />
<ul>
<li><NavLink to="/home" className={({ isActive }) => { return isActive ? 'myActive' : ''} }>首页</NavLink></li>
<li><NavLink to="/login">登录页</NavLink></li>
<li><NavLink to="/user">用户中心</NavLink></li>
</ul>
<hr />
{/* 使用useRoutes hook 根据路由表生成路由配置 */}
{useRoutes(routes)}
</div>
)
}
后代路由
在路由配置对象中通过添加 children
属性配置后代路由
步骤
1. 创建二级路由组件页面
2. 在路由配置对象中通过添加 `children` 属性
示例:
routes=[
{
path:'/home',
children:[
{
path:'/home/news',
element:<News/>
}
]
}
]
简写:
path:'news',前面不能有 / ,否则会认为是一级路由
Outlet组件
父路由的子路由出口通过 Outlet
组件渲染
父路由元素中通过使用 <Outlet> 渲染子路由元素来显示嵌套 UI。
如果父路由精确匹配将渲染子索引路由,没有索引路由不渲染任何内容。
使用案例(首页-关于-新闻)
入口文件:src->index.js
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>
<App />
</BrowserRouter>
);
主组件:src->App.jsx
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'
export default function App() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<NavLink className='list-group-item' to='/about'>About</NavLink>
<NavLink className='list-group-item' to='/home'>Home</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 一级路由,路由组件要展示的位置 */}
{useRoutes(routes)}
</div>
</div>
</div>
</div>
</div>
)
}
路由配置对象:src->routes->index.js
import { Navigate} from 'react-router-dom'
import About from '../pages/About/About'
import Home from '../pages/Home/Home'
import News from '../pages/Home/News/News'
import Messages from '../pages/Home/Messages/Messages'
const routes = [
{
path:'/home',
element: <Home />,
children:[
{
// 简写,注意前面不能有 / ,否则就认为是一级路由
path: 'news',
element: <News />
},
{
// path:'/home/message', // 完整路径写法
path: 'message',
element: <Messages />
},
// 重定向
{
path: '/home',
element: <Navigate to='/home/news' />
}
]
},
{
path: '/about',
element: <About />
},
{
path: '/',
element: <Navigate to='/home' />
},
]
export default routes
路由子组件:src->pages->Home->index.jsx
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="/home/message">Message</NavLink>
</li>
</ul>
{/* 二级路由组件展示位置 */}
<Outlet />
</div>
</div>
)
}
路由子组件:src->pages->About->index.jsx
import React from 'react'
export default function About() {
return (
<div>About</div>
)
}
路由子组件:src->pages->Home->News->index.jsx
import React from 'react'
export default function News() {
return (
<div>News</div>
)
}
路由子组件:src->pages->Home->Message->index.jsx
import React from 'react'
export default function Messages() {
return (
<div>Messages</div>
)
}
编程式导航
useNavigate函数
useNavigate 返回值
返回一个用编程方式导航的函数,可用于例如提交表单。
useNavigate 参数
1. 传入一个跳转地址'/user' 如 navigate('/user')
2. 传入第二个可选参数 对象
3. 传想要入 history 堆栈的增量,例如 navigate(-1) 相当于点击后退按钮。
对象中有replace属性,值为布尔值
当 replace:true 时,该次路由跳转记录替换history堆栈中最后一次路由记录。
当 replace:false 时,该次路由跳转记录新增到history堆栈中最后一次路由记录之后(默认)。
// 默认 新增
<button onClick={ ()=>{navigate('/about')}}>About</button>
// 设置replace属性 替换掉之前的历史记录
<button onClick={()=>{navigate('/about',{replace:true})}}>About</button>
// 设置前进后推
<button onClick={()=>{navigate(1)}}>前进</button>
<button onClick={()=>{navigate(-1)}}>后退</button>
<button onClick={()=>{navigate(2)}}>前进2步</button>
路由传参
path参数
参数通过url路径传递: /user/1/atguigu
基本使用
- 在需要路由跳转的地方传递实参:
<Link to='/home/message/path/1/atguigu'>path参数的传递和接收</Link>
- 在路由配置对象中设置占位符
children:[
{
// path-params参数需要配置路径占位符
path:'/home/message/path/:id/:school',
element:<PathTest/>
}
]
- 在另一个路由组件中接收数据:使用
useParams()
函数接收
// 解构接收path参数
let {id, school} = useParams();
useParams()
useParams 返回值
返回当前 URL 与 <Route path> 匹配的动态参数的键值对对象,对象中包含子路由继承父路由的所有参数。
import React from 'react'
import { useParams } from 'react-router-dom'
export default function PathTest() {
let res = useParams();
// 路由配置中 path:'/home/message/path/:id/:school',
console.log('res: ', res); // {id: '1', school: 'atguigu'}
// 解构接收使用
let { id, school } = useParams();
return (
<div>
<h3>PathTest</h3>
<p>path id: {id}</p>
<p>path school: {school}</p>
</div>
)
}
query参数
在URL中通过 yuluochenxiao.top?key=value&key2=value2&...形式传递参数
基本使用
- 在需要路由跳转的地方传递实参:
<Link to="/home/message/query?username=atguigu&age=20">query参数的传递</Link>
- 在路由配置对象中无需多余配置
{
path:'/home/message/query',
element:<QueryTest/>
}
- 在另一个路由组件中接收数据:使用
useSearchParams()
函数接收
let [searchQuery, setSearchQuery] = useSearchParams();
let username = searchQuery.get('username')
let age = searchQuery.get('age')
// 设置query参数
setSearchQuery('?username=尚硅谷&age=100')
useSearchParams()
useSearchParams() 用于读取和修改 URL 中当前 location 的查询字符串(query string)
获取数据:
let [searchQuery, setsearchQuery] = useSearchParams()
let username = searchQuery.get('username')
修改query
setSearchQuery('?username=尚硅谷&age=100')
import React from 'react'
import { useSearchParams } from 'react-router-dom'
export default function QueryTest() {
let res = useSearchParams();
console.log('res: ', res)
// res是一个数组:[URLSearchParams, ƒ] 如同useState的返回值
// 但不同的是 URLSearchParams 是一个对象,URLSearchParams对象的原型上URLSearchParams.Prototype存在get方法
// 调用get方法可以获取到数据
let [searchQuery, setSearchQuery] = useSearchParams();
let username = searchQuery.get('username');
let age = searchQuery.get('age');
return (
<div>
<h3>QueryTest</h3>
<p>query username: {username}</p>
<p>query age: {age}</p>
<p><button onClick={() => {
setSearchQuery('?username=尚硅谷&age=100')
}}>设置query参数</button></p>
</div>
)
}
state参数
state参数可以传递复杂数据类型,state参数不会出现在地址栏中
基本使用
- 在需要路由跳转的地方传递实参:
<Link to='/home/message/state' state={数据}>state参数</Link>
- 在路由配置对象中无需多余配置
{
path:'/home/message/state',
element:<StateTest/>
}
- 在另一个路由组件中接收数据:使用
useLocation()
函数接收
let { state: { id, title, isDone } } = useLocation();
useLocation()
此 hook 返回当前 location 对象,可用于在当前 location 改变时执行一些副作用
import React from 'react'
import { useLocation } from 'react-router-dom'
export default function StateTest() {
let x = useLocation();
console.log('x: ', x);// {pathname: '/home/message/state', search: '', hash: '', state: {…}, key: 'sywlhcu2'}
// 从state中解构获取数据
let { state: { id, title, isDone } } = useLocation();
return (
<div>
<h3>StateTest</h3>
<p>state id: {id}</p>
<p>state title: {title}</p>
<p>state isDone: {isDone ? '已完成' : '未完成'}</p>
</div>
)
}
路由懒加载
依赖于 react包中的 lazy函数、Suspense组件
基本使用
- 在路由配置对象中导入包:src->routes->index.js
import {lazy, Suspense} from 'react'
- 使用lazy函数加载路由组件:src->routes->index.js
const About = lazy(() => import("../pages/About"));
const Message = lazy(() => import("../pages/Message"));
const PathTest = lazy(() => import("../pages/PathTest"));
const QueryTest = lazy(() => import("../pages/QueryTest"));
const StateTest = lazy(() => import("../pages/StateTest"));
- 封装Suspense加载组件的函数:src->routes->index.js
使用 `Suspense组件` 包裹路由组件,设置`fallback`属性,当懒加载的路由组件没有加载完成的时候,显示的内容
function load(Com) {
return (
<Suspense fallback={<div>组件正在加载中....</div>}>
<Com />
</Suspense>
)
}
// 调用load函数
element: load(Message)
路由模式
html5 history模式 和 hash路由模式
BrowserRouter: html5-history模式
示例:localhost:3000/home/news
HashRouter: hash路由模式 [了解],使用#锚点,可以解决部分根路径问题 ./ 被解析为根 /
示例:localhost:3000/#/home/news