补充一个:类组件,无法使用 useNavigate 这个hook函数,所以,编写一个高阶组件
javascript
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'
// 类组件,无法使用 useNavigate 这个hook函数,
// 所以,编写一个高阶组件,
function withRouter(WrapperComponent) {
// 返回一个函数式组件
return function (props) {
// 1、导航的跳转
const navigate = useNavigate()
// 2、动态路由的参数,如:/detail/:id
const params = useParams()
// 3、查询字符串参数,如:/user?name=why&age=18
// location、query 是两种方式
const location = useLocation()
console.log('location=', location)
const [searchParams, setSearchParams] = useSearchParams()
const query = Object.fromEntries(searchParams)
console.log('query=', query)
const router = { navigate, params, query }
// props原样传回去
return <WrapperComponent {...props} router={router}></WrapperComponent>
}
}
export default withRouter
使用这个 withRouter
javascript
import React, { PureComponent } from 'react'
// Link:用于声明式导航(类似 <a> 标签,但不会触发页面刷新)
// OutLet:子路由的占位符,<Outlet /> 是子路由组件的渲染位置,根据当前 URL 路径,自动匹配对应的子路由组件并渲染到此处。
// useNavigate:Hook,用于编程式导航(只能在函数组件中使用)
import { Link, Outlet, useNavigate } from 'react-router-dom'
import { withRouter } from '../hoc'
export class Home extends PureComponent {
navigateTo(path) {
const { navigate } = this.props.router
navigate(path)
}
render() {
return (
<div>
<h1>HomePage</h1>
<div className="home-nav">
<Link to="/home/recommend">推荐</Link>
<Link to="/home/ranking">排名</Link>
<button onClick={e => this.navigateTo('/home/songmenu')}>歌单</button>
</div>
` 有了占位符,才能显示子路由在哪个位置渲染,
没有占位符,子路由根本就不会渲染到页面上。`
<Outlet />
</div>
)
}
}
// export default Home
export default withRouter(Home)
补充一个
- 动态路由参数
javascript
定义:
{ path: '/detail/:id', element: <Detail /> }
使用:
在 Detail 组件中通过 useParams() 获取参数:
import { useParams } from 'react-router-dom'
function Detail() {
const { id } = useParams() // id = 路径中的动态值(如 "123")
return <div>Detail ID: {id}</div>
}
- 查询参数
javascript
定义:
<Link to="/user?name=why&age=18">用户</Link>
使用:
在 User 组件中通过 useSearchParams() 获取:
import { useSearchParams } from 'react-router-dom'
function User() {
const [searchParams] = useSearchParams()
const name = searchParams.get('name') // "why"
const age = searchParams.get('age') // "18"
return <div>User: {name}, Age: {age}</div>
}
安装及引入配置:npm i react-router-dom
src/index.js
javascript
// 导入 React 核心库和 Suspense 组件(用于代码分割和懒加载)
import React, { Suspense } from 'react'
// 导入 ReactDOM 的客户端渲染方法(React 18+ 新 API)
import ReactDOM from 'react-dom/client'
// 导入根组件 App
import App from './App'
`1、安装路由:npm i react-router-dom`
`2、使用 HashRouter 的路由模式`
import { HashRouter } from 'react-router-dom'
// 创建根渲染节点,绑定到 HTML 中 id="root" 的 DOM 元素
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
// <React.StrictMode>
// {/*
// 3、HashRouter 包裹 <App />
// 4、配置 Routes,就是映射关系:path -> component
// */}
// <HashRouter>
// <App />
// </HashRouter>
// </React.StrictMode>
// <HashRouter>
// <App />
// </HashRouter>
` 打包的时候,如果要对 About、Login 分别单独打包处理
* Suspense 的作用;
* 配合 React.lazy 实现组件懒加载,如:const About = React.lazy(() => import('../pages/About'))
* 当子组件尚未加载完成时,显示 fallback 的加载状态(此处是 <h3>Loading...</h3>)
* 打包工具(如:Webpack)会为动态导入的组件生成单独 chunk。`
<HashRouter>
<Suspense fallback={<h3>Loading...</h3>}>
<App />
</Suspense>
</HashRouter>
)
如果是类组件
App.jsx
javascript
import React, { PureComponent } from 'react'
// 配置映射关系
import { Routes, Route, Link, NavLink, Navigate } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Login from './pages/Login'
import NotFound from './pages/NotFound'
import Category from './pages/Category'
import Order from './pages/Order'
import HomeRecommend from './pages/HomeRecommend'
import HomeRanking from './pages/HomeRanking'
export class App extends PureComponent {
render() {
return (
<div className="app">
<div className="header">
<span>header</span>
<div className="nav">
`NavLink标签,会带一个"active"的class,可以通过这个class编写样式`
<NavLink to="/home">首页</NavLink>
<NavLink to="/about">关于</NavLink>
`也可以这样`
<NavLink to="/home" style={({isActive}) => ({color: isActive ? 'red' : ''})}>首页</NavLink>
<NavLink to="/about" className={({isActive}) => (isActive ? 'link-active' : '')}>关于</NavLink>
<Link to="/home">首页</Link>
<Link to="/about">关于</Link>
</div>
</div>
<hr />
<div className="content">
`配置映射关系`
<Routes>
<Route path="/" element={<Navigate to="/home" />}></Route>
<Route path="/home" element={<Home />}>
<Route path="/home" element={<Navigate to="/home/recommend" />}></Route>
<Route path="/home/recommend" element={<HomeRecommend />}></Route>
<Route path="/home/ranking" element={<HomeRanking />}></Route>
</Route>
<Route path="/about" element={<About />}></Route>
<Route path="/order" element={<Order />}></Route>
<Route path="/category" element={<Category />}></Route>
<Route path="/login" element={<Login />}></Route>
`【* :代表通配符,当以上所有路径都没有匹配的时候,就会匹配到这个 * 】`
<Route path="*" element={<NotFound />}></Route>
</Routes>
</div>
<hr />
<div className="footer">footer</div>
</div>
)
}
}
export default App
如果是函数式组件(不使用路由配置)
Routes
包裹所有 Route 的容器,负责路由匹配Route
定义,路径与组件的映射关系Link
声明式导航组件(类似 标签,无刷新跳转)NavLink
增强版 Link,可添加激活状态的样式Navigate
用于重定向(如:默认跳转)useNavigate Hook
用于编程式导航(如:按钮点击跳转)
App.jsx
javascript
import React from 'react'
// 配置映射关系
import { Routes, Route, Link, NavLink, Navigate, useNavigate } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Login from './pages/Login'
import NotFound from './pages/NotFound'
import Category from './pages/Category'
import Order from './pages/Order'
import User from './pages/User'
import HomeRecommend from './pages/HomeRecommend'
import HomeRanking from './pages/HomeRanking'
import HomeSongMenu from './pages/HomeSongMenu'
import Detail from './pages/Detail'
export function App(props) {
const navigate = useNavigate()
function navigateTo(path) {
navigate(path)
}
return (
<div className="app">
<div className="header">
<span>header</span>
<div className="nav">
`声明式导航`
<Link to="/home">首页</Link>
<Link to="/about">关于</Link>
<Link to="/login">登录</Link>
`编程式导航`
<button onClick={e => navigateTo('/category')}>分类</button>
<span onClick={e => navigateTo('order')}>订单</span>
` 带查询参数的导航
在 User 组件中通过 useSearchParams() 获取:
const [searchParams] = useSearchParams()
const name = searchParams.get('name') // "why"
const age = searchParams.get('age') // "18" `
<Link to="/user?name=why&age=18">用户</Link>
</div>
</div>
<hr />
<div className="content">
`配置映射关系`
<Routes>
<Route path="/" element={<Navigate to="/login" />}></Route>
<Route path="/login" element={<Login />}></Route>
<Route path="/home" element={<Home />}>
`默认子路由重定向`
<Route path="/home" element={<Navigate to="/home/recommend" />}></Route>
<Route path="/home/recommend" element={<HomeRecommend />}></Route>
<Route path="/home/ranking" element={<HomeRanking />}></Route>
<Route path="/home/songmenu" element={<HomeSongMenu />}></Route>
</Route>
<Route path="/about" element={<About />}></Route>
<Route path="/order" element={<Order />}></Route>
<Route path="/category" element={<Category />}></Route>
`路由传参 - 方式1 - 动态路由参数
如:
访问 /detail/123 时,Detail 组件可通过 useParams() 获取 id 参数:
const { id } = useParams() // id = "123" 。`
<Route path="/detail/:id" element={<Detail />}></Route>
`查询参数接收页`
<Route path="/user" element={<User />}></Route>
`【* :代表通配符,当以上所有路径都没有匹配的时候,就会匹配到这个 * 】`
<Route path="*" element={<NotFound />}></Route>
</Routes>
</div>
<hr />
<div className="footer">footer</div>
</div>
)
}
export default App
函数式组件(使用路由配置)
App.jsx
javascript
import React from 'react'
// 配置映射关系
import { Routes, Route, Link, NavLink, Navigate, useNavigate, useRoutes } from 'react-router-dom'
import routes from './router'
export function App(props) {
const navigate = useNavigate()
function navigateTo(path) {
navigate(path)
}
return (
<div className="app">
<div className="header">
<span>header</span>
<div className="nav">
<Link to="/home">首页</Link>
<Link to="/about">关于</Link>
<Link to="/login">登录</Link>
<button onClick={e => navigateTo('/category')}>分类</button>
<span onClick={e => navigateTo('order')}>订单</span>
<Link to="/user?name=why&age=18">用户</Link>
</div>
</div>
<hr />
<div className="content">
{/* 配置映射关系 */}
{/* 使用路由配置 */}
{useRoutes(routes)}
</div>
<hr />
<div className="footer">footer</div>
</div>
)
}
export default App
router/index.js 路由配置文件
javascript
import { Navigate } from 'react-router-dom'
import React from 'react'
import Home from '../pages/Home'
`打包的时候,如果要对 About、Login 分别单独打包处理,就可以使用懒加载。`
// import About from '../pages/About'
// import Login from '../pages/Login'
import NotFound from '../pages/NotFound'
import Category from '../pages/Category'
import Order from '../pages/Order'
import User from '../pages/User'
import HomeRecommend from '../pages/HomeRecommend'
import HomeRanking from '../pages/HomeRanking'
import HomeSongMenu from '../pages/HomeSongMenu'
import Detail from '../pages/Detail'
`
打包的时候,如果要对 About、Login 分别单独打包处理,就可以使用懒加载
为什么使用这种形式能够单独打包呢?
因为,这是,webpack的特性。`
/*
还需要这样处理一下!!!
// 打包的时候,如果要对 About、Login 分别单独打包处理
<HashRouter>
<Suspense fallback={<h3>Loading...</h3>}>
<App />
</Suspense>
</HashRouter>
*/
const About = React.lazy(() => import('../pages/About'))
const Login = React.lazy(() => import('../pages/Login'))
const routes = [
{
path: '/',
element: <Navigate to="/home" />
},
{
path: '/home',
element: <Home />,
children: [
{
path: '/home',
element: <Navigate to="/home/recommend" />
},
{
path: '/home/recommend',
element: <HomeRecommend />
},
{
path: '/home/ranking',
element: <HomeRanking />
},
{
path: '/home/songmenu',
element: <HomeSongMenu />
}
]
},
{
path: '/about',
element: <About />
},
{
path: '/login',
element: <Login />
},
{
path: '/category',
element: <Category />
},
{
path: '/order',
element: <Order />
},
{
path: '/detail/:id',
element: <Detail />
},
{
path: '/user',
element: <User />
},
{
path: '*',
element: <NotFound />
}
]
export default routes