前端路由的核心:改变URL,但是页面不进行整体的刷新。官网移步Home v6.21.3 | React Router
一、 Router的基本使用
React Router的版本4开始,路由不再集中在一个包中进行管理了:
- react-router是router的核心部分代码;
- react-router-dom是用于浏览器的;
- react-router-native是用于原生应用的;
安装react-router-dom会自动帮助我们安装react-router的依赖;
1.1 安装
shell
npm install react-router-dom
路由的核心是映射关系,路径对应组件
1.2 BrowserRouter和HashRouter/Routers和route
- BrowserRouter使用history模式;
- HashRouter使用hash模式;像Vue中的hash一样路径中出现/#
<Routes>
都会匹配当前位置的一组子路由。Route
用于路径的匹配;- path:匹配路径
element
路由与 URL 匹配时要渲染的 React 元素- 其他属性参考官网 入口文件
js
import React, { StrictMode } from "react"
import ReactDOM from 'react-dom/client';
import { HashRouter } from 'react-router-dom'
import App from './App.jsx'
const root = ReactDOM.createRoot(document.getElementById("root"))
// HashRouter可以更换成普通的BrowserRouter模式
root.render(
<StrictMode>
<HashRouter>
<App />
</HashRouter>
</StrictMode>
)
App.jsx
jsx
import React, { PureComponent } from 'react'
import { Route, Routes } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'
export class App extends PureComponent {
render() {
return (
<div className='app'>
<header className='Header'>
顶部导航栏
</header>
<div className='main'>
主体部分
<hr />
展示路由
<div style={{border:"1px solid red",height:"200px"}}>
<Routes>
<Route path='/home' element={<Home/>}></Route>
<Route path='/about' element={<About/>}></Route>
<Route path='/user' element={<User/>}></Route>
</Routes>
</div>
</div>
<footer className='footer'>
页脚
</footer>
</div>
)
}
}
export default App

到这里已经实现了效果,但是每次都需要手动在地址栏去修改路由地址才能渲染对应的组件,那有没有一种办法可以进行界面点击跳转呢?当然可以添加了
1.3Link和NavLink:
- 通常路径的跳转是使用Link组件,最终会被渲染成a元素;
- NavLink是在Link基础之上增加了一些样式属性;
- to属性:Link中最重要的属性,用于设置跳转到的路径;
Link使用
App.jsx
jsx
import { Link, Route, Routes } from 'react-router-dom'
//other import
export class App extends PureComponent {
render() {
return (
<div className='app'>
<header className='Header'>
顶部导航栏
<h1>
<Link to="/home">
首页
</Link>
</h1>
<h1>
<Link to="/about">
关于
</Link>
</h1>
<h1>
<Link to="/user">
我的
</Link>
</h1>
</header>
{/* other code */}
</div>
)
}
}
export default App
虽然点击可以切换但是如何高亮显示我们选中的是哪一个呢?这时候就需要用到另外一个组件!!!
NavLink使用
这里点击激活路由就让a标签显示为绿色的我来修改一下:将<Link>
修改为<NavLink>
默认匹配成功时,NavLink就会添加上一个动态的active class;所以直接编写样式
css
.active{
color: green
}
当然了如果你不想使用导入css文件的话也可以使用函数来修改:作为了解
这里是动态添加样式,也可以动态添加class进行修改,搭配样式选择器来修改样式
jsx
//...
<h3>
<NavLink to="/home" style={({isActive})=>({color:isActive?"black":""})}> 首页</NavLink>
<NavLink to="/about" style={({isActive})=>({color:isActive?"black":""})}> 关于</NavLink>
<NavLink to="/user" style={({isActive})=>({color:isActive?"black":""})}> 我的</NavLink>
</h3>
//...
1.4 Navigate导航
如果我们在路径中只输入了一个根路径界面就匹配不到对应路由进行渲染展示,因此我这里新增一个/
作为路径搭配Navigate
导航来进行跳转 借用上文示例 App.jsx
jsx
<div style={{ border: "1px solid red", height: "200px" }}>
<Routes>
//这里使用方式
<Route path='/' element={<Navigate to="/home"/>}></Route>
<Route path='/home' element={<Home />}></Route>
<Route path='/about' element={<About />}></Route>
<Route path='/user' element={<User />}></Route>
{/* 这里使用通配符,如果用户子地址栏输入一个不存在的路由地址那么就进行匹配并且渲染element元素进行展示,当然你也可以进行抽离 */}
<Route path='*' element={<div>页面丢失了</div>}></Route>
</Routes>
</div>
二、Router的路由嵌套
学习过Vue的都知道,vue-router存在路由嵌套关系。那我们来看一下ReactRouter是怎样实现路由嵌套的
新建两个页面src/pages/HomeSon1.jsx和src/pages/HomeSon2.jsx
修改入口文件App.jsx
jsx
import React, { PureComponent } from 'react'
import { NavLink, Navigate, Route, Routes } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'
import HomeSon1 from './pages/HomeSon1'
import HomeSon2 from './pages/HomeSon2'
import "./style.css"
export class App extends PureComponent {
render() {
return (
<div className='app'>
<header className='Header'>
顶部导航栏
<h3>
<NavLink to="/home" > 首页</NavLink>
<NavLink to="/about" > 关于</NavLink>
<NavLink to="/user" > 我的</NavLink>
</h3>
</header>
<div className='main'>
主体部分
<hr />
展示路由
<div style={{ border: "1px solid red", height: "200px" }}>
<Routes>
<Route path='/' element={<Navigate to="/home"/>}></Route>
<Route path='/home' element={<Home />}>
<Route path='/home/HomeSon1' element={<HomeSon1/>}></Route>
<Route path='/home/HomeSon2' element={<HomeSon2/>}></Route>
</Route>
<Route path='/about' element={<About />}></Route>
<Route path='/user' element={<User />}></Route>
{/* 这里使用通配符,如果用户子地址栏输入一个不存在的路由地址那么就进行匹配并且渲染element元素进行展示,当然你也可以进行抽离 */}
<Route path='*' element={<div>页面丢失了</div>}></Route>
</Routes>
</div>
</div>
<footer className='footer'>
页脚
</footer>
</div>
)
}
}
export default App
修改src/Home.jsx
jsx
import React, { PureComponent } from 'react'
import { Outlet,NavLink } from 'react-router-dom'
export class Home extends PureComponent {
render() {
return (
<div>
首页页面
<div>
导航:
<NavLink to="/home/HomeSon1" > 首页嵌套1</NavLink>------
<NavLink to="/home/HomeSon2" > 首页嵌套2</NavLink>
</div>
</div>
)
}
}
export default Home
这时候点击会发现页面中并没有出现我们想要的样子内容。 因此我们需要使用<Outlet>
父路由元素中应使用 <Outlet>
来呈现其子路由元素。这样就可以在呈现子路由时显示嵌套用户界面。如果父路由完全匹配,则会呈现子索引路由;如果没有索引路由,则不会呈现任何内容
jsx
//其实本质就是作为站位元素
//....
export class Home extends PureComponent {
render() {
return (
<div>
首页页面
...
<div>
展示子路由
<Outlet/>
</div>
</div>
)
}
}
//...
此时访问路径会发现还有一个小问题就是我们访问/home路径时候界面并没有出现子路由界面,因此我们要进行优化使用Navigate App.jsx
jsx
<Route path='/home' element={<Home />}>
<Route path='/home' element={<Navigate to="/home/HomeSon1"/>}></Route>
<Route path='/home/HomeSon1' element={<HomeSon1/>}></Route>
<Route path='/home/HomeSon2' element={<HomeSon2/>}></Route>
</Route>
三、Router的代码跳转
目前我们实现的跳转主要是通过Link或者NavLink进行跳转的,实际上我们也可以通过JavaScript代码
进行跳转 这里我们要用到Hooks中的useNavigate
但是Hooks只能在函数式组件中使用,所以我们来改造一下App.jsx
jsx
import { Navigate, Route, Routes, useNavigate } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'
import HomeSon1 from './pages/HomeSon1'
import HomeSon2 from './pages/HomeSon2'
export function App() {
const navigate = useNavigate()
return (
<div className='app'>
<header className='Header'>
顶部导航栏
<h3>
<button onClick={e=>navigate("/home")}>首页</button>
<button onClick={e=>navigate("/about")}>关于</button>
<button onClick={e=>navigate("/user")}>我的</button>
</h3>
</header>
<div className='main'>
主体部分
<hr />
展示路由
<div style={{ border: "1px solid red", height: "200px" }}>
<Routes>
<Route path='/' element={<Navigate to="/home" />}></Route>
<Route path='/home' element={<Home />}>
<Route path='/home' element={<Navigate to="/home/HomeSon1" />}></Route>
<Route path='/home/HomeSon1' element={<HomeSon1 />}></Route>
<Route path='/home/HomeSon2' element={<HomeSon2 />}></Route>
</Route>
<Route path='/about' element={<About />}></Route>
<Route path='/user' element={<User />}></Route>
{/* 这里使用通配符,如果用户子地址栏输入一个不存在的路由地址那么就进行匹配并且渲染element元素进行展示,当然你也可以进行抽离 */}
<Route path='*' element={<div>页面丢失了</div>}></Route>
</Routes>
</div>
</div>
<footer className='footer'>
页脚
</footer>
</div>
)
}
export default App
但是有些就喜欢使用类组件,那么我们还有第二种方式封装一个高阶组件在类组件中使用
下面继续改造App.jsx文件 将导航抽取走

新建HeaderNav.jsx导航文件
jsx
import React, { PureComponent } from 'react'
import { useNavigate } from 'react-router-dom'
export class HeaderNav extends PureComponent {
render() {
const { navigate } = this.props.router
return (
<div>
<button onClick={e => navigate("/home")}>首页</button>
<button onClick={e => navigate("/about")}>关于</button>
<button onClick={e => navigate("/user")}>我的</button>
</div>
)
}
}
//高阶组件可以进行拆分
function HocWithRouter(WrapperComponent) {
return function (props) {
const navigate = useNavigate()
const router = { navigate }
return <WrapperComponent {...props} router={router} />
}
}
export default HocWithRouter(HeaderNav)
这样就实现了功能。
四、Router的参数传递
4.1 动态路由传参使用Hooks-useParams
动态路由的概念指的是路由中的路径并不固定:
/search
的path对应一个组件Search;- 如果我们将path在Route匹配时写成
/search/:id
,那么/search/abc
、/search/123
都可以匹配到该Route,并且进行显示; - 这个匹配规则,我们就称之为动态路由; 通常情况下,使用动态路由可以为路由传递参数。 App.jsx
jsx
import { Navigate, Route, Routes,NavLink } from 'react-router-dom'
import Search from './pages/Search'
import "./style.css"
export function App() {
return (
<div className='app'>
<h3>
<NavLink to="/search/ceshi" > 动态路由</NavLink>------
</h3>
展示路由
<div style={{ border: "1px solid red", height: "200px" }}>
<Routes>
<Route path='/' element={<Navigate to="/search" />}></Route>
<Route path='/search/:id' element={<Search/>}></Route>
</Routes>
</div>
</div>
)
}
export default App
Search.jsx
jsx
import React, { PureComponent } from 'react'
import { useNavigate, useParams } from 'react-router-dom';
//高阶组件可以进行拆分
function hocWithRouter(WrapperComponent) {
return function (props) {
const navigate = useNavigate()
const params = useParams()
const router = { navigate,params }
return <WrapperComponent {...props} router={router} />
}
}
export class Search extends PureComponent {
render() {
const { params }=this.props.router
return (
<div>
我是搜索界面
参数传递:{params.id}
</div>
)
}
}
export default hocWithRouter(Search)
4.2 search传递参数 Hooks-useLocation
App.jsx
jsx
import { Route, Routes, NavLink } from 'react-router-dom'
import Detail from './pages/Detail'
export function App() {
return (
<div className='app'>
<h3>
<NavLink to="/detail?name=林夕&age=18">search传参数</NavLink>
</h3>
展示路由
<div style={{ border: "1px solid red", height: "200px" }}>
<Routes>
<Route path='/detail' element={<Detail />}></Route>
</Routes>
</div>
</div>
)
}
export default App
Detail.jsx
jsx
import React, { PureComponent } from 'react'
import { useNavigate, useParams, useLocation, useSearchParams } from 'react-router-dom';
export class Detail extends PureComponent {
render() {
const { location,query } = this.props.router
return (
<div>
{query.name}-{query.age}
</div>
)
}
}
//高阶组件使用Hooks
function hocWithRouter(WrapperComponent) {
return function (props) {
const navigate = useNavigate()
// 动态路由
const params = useParams()
// 查询字符串参数
const location = useLocation()
const [searchParams]=useSearchParams()
const query=Object.fromEntries(searchParams)
console.log(query);
const router = { navigate, params, location,query }
return <WrapperComponent {...props} router={router} />
}
}
export default hocWithRouter(Detail)
五、Router的配置方式- useRoutes
将上文的Routes和Route转化成单独的js文件去,同时使用Hooks
jsx
<Routes>
<Route path='/' element={<Navigate to="/home"/>}></Route>
<Route path='/home' element={<Home />}></Route>
<Route path='/about' element={<About />}></Route>
<Route path='/user' element={<User />}></Route>
<Route path='*' element={<div>页面丢失了</div>}></Route>
</Routes>
抽取成单独的js文件 新建router.js
js
import Home from './pages/Home'
import About from './pages/About'
import User from './pages/User'
import HomeSon1 from './pages/HomeSon1'
import HomeSon2 from './pages/HomeSon2'
import { Navigate } from 'react-router-dom'
const routes=[
{
path:"/",
element:<Navigate to="/home" />
},
{
path:"/home",
element:<Home />,
children:[
{
path:"/home",
element:<Navigate to="/home/HomeSon1" />
},
{
path:"/home/HomeSon1",
element:<HomeSon1 />
},
{
path:"/home/HomeSon2",
element:<HomeSon2 />
},
]
},
{
path:"/about",
element:<About/>
},
{
path:"/user",
element:<User />
},
{
path:"*",
element:<div>页面丢失了</div>
},
]
export default routes
App.jsx
jsx
import routes from './router'
//...other code
展示路由
<div style={{ border: "1px solid red", height: "200px" }}>
{useRoutes(routes)}
</div>