Redux (八) 路由React-router、嵌套路由、路由传参、路由懒加载
文章目录
- 一、React-Router的基本使用
- 1. 安装及基本使用(路由映射配置)
- 2. 路由跳转Link与NavLink
- 3. Navigate导航
- 4. 处理路径不存在的情况
- 二、嵌套路由
- 三、手动跳转 (类似编程式路由导航)
- 1. 函数式组件
- 2. 类组件实现手动跳转
- 四、路由传参
- 1. 路径设置占位符(params)
- 2. search传递参数
- 五、路由配置到单独的文件中(useRoutes)
- 六、路由懒加载
一、React-Router的基本使用
1. 安装及基本使用(路由映射配置)
安装:
npm install react-router-dom
react-router最主要的时提供了一系列重要的组件,
(1) 设置路由模式
路由有两种模式:hash和history,对应react-router里的两个组件HashRouter
和BrowserRouter
HashRouter
: 设置应用的路由采用hash模式
import { HashRouter } from 'react-router-dom';
root.render(
<HashRouter>
<App />
</HashRouter>
);
BrowserRouter
: 设置应用的路由采用history模式
import { HashRouter, BrowserRouter } from 'react-router-dom';
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
hash模式地址有#
,history模式没有,开发更习惯用hash模式。
(2) 路由的映射配置
Routes
组件:包裹所有的Route
Route
: 用于设置路径与组件的对应关系,有两个属性:
path属性: 用于设置路径
element属性: 设置路径对应的组件.
import { Route, Routes } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
export class App extends PureComponent {
render () {
return (
<div>
<div className="header">
<h2>Header</h2>
</div>
<hr />
<div className='content'>
<Routes>
{/* /home路径对应Home组件 */}
<Route path='/home' element={<Home />} />
<Route path='/about' element={<About />} />
</Routes>
</div>
<hr />
<div className="footer">
<h2>Footer</h2>
</div>
</div>
)
}
}
2. 路由跳转Link与NavLink
Link
和NavLink
有点像vue里的router-link
,to属性指定跳转的路径。最终都会被渲染称a元素。
两者的区别是:NavLink
在被选中时,会自动加上active
的类名。可以借此设置一些样式
点击不同的链接:
3. Navigate导航
该组件用于路由的重定向,当这个组件出现时,会跳转到对应的to
路径中。
比如用户在登录页,当点击登录按钮时,自动跳转到home页面。
import { Navigate } from 'react-router-dom'
export class Login extends PureComponent {
constructor() {
super()
this.state = {
isLogin: false
}
}
login () {
this.setState({ isLogin: true })
}
render () {
const { isLogin } = this.state
return (
<div>
<h3>登录界面</h3>
{/* isLogin为true,则跳转到home页面,否则显示按钮 */}
{isLogin ? <Navigate to='/home' /> : <button onClick={e => this.login()}>登录</button>}
</div>
)
}
}
export default Login
还有路由重定向问题;当路径为/
时,需要重定向到别的页面,比如重定向到首页。
<div className='content'>
<Routes>
{/* 重定向方式一: */}
<Route path='/' element={<Navigate to='/login' />} />
{/* 重定向方式二: */}
<Route path='/' element={<Home />} />
<Route path='/home' element={<Home />} />
<Route path='/about' element={<About />} />
<Route path='/login' element={<Login />} />
</Routes>
</div>
4. 处理路径不存在的情况
随便输入一个不存在的网址:
http://localhost:3000/#/abc
针对这种情况,一般会单独写一个NotFound
的页面,提示用户页面不存在。
我们通过 设置路径为通配符*
来映射到NotFound
页面。
<div className='content'>
<Routes>
{/* 重定向方式一: */}
<Route path='/' element={<Navigate to='/login' />} />
{/* 重定向方式二: */}
<Route path='/' element={<Home />} />
<Route path='/home' element={<Home />} />
<Route path='/about' element={<About />} />
<Route path='/login' element={<Login />} />
{/* 路径一路匹配,匹配不到,说明路径不存在时,
最后通过通配符*,跳转到NotFound页面 */}
<Route path='/*' element={<NotFound />} />
</Routes>
</div>
二、嵌套路由
在content里显示Home组件的内容,Home组件里又显示推荐、排行等子组件。
现在要实现点击推荐、排行,显示对应的组件。
App.jsx
中
<div className='content'>
<Routes>
{/* 重定向 */}
<Route path='/' element={<Navigate to='/home' />} />
<Route path='/home' element={<Home />} >
{/* 跳转到首页时,自动重定向到推荐页面 */}
<Route path='/home' element={<Navigate to='/home/recommend' />} />
{/* 嵌套路由 */}
<Route path='/home/recommend' element={<HomeRecommend />} />
<Route path='/home/rank' element={<HomeRanking />} />
<Route path='/home/songmenu' element={<HomeSongMenu />} />
</Route>
<Route path='/about' element={<About />} />
<Routes/>
<div/>
嵌套路由就是在Route
的双标签里,写子组件的路由Route
。
由于是Home页面内嵌套显示其他页面,所以Home.js
:
return (
<div>
<h3>Home</h3>
<Link to='/home/recommend'>推荐</Link>
<Link to='/home/rank'>排行</Link>
<button>歌曲目录</button>
{/* 占位,推荐,排行,歌曲目录的子组件在这儿展示 */}
<Outlet />
</div>
)
}
Outlet
用于在父路由组件中子路由的占位,
三、手动跳转 (类似编程式路由导航)
react-router-dom
提供了函数useNavigate
,调用它会返回一个函数,执行该函数并将跳转路径传入就会实现跳转。但是,useNavigate
这个hook只能在函数式组件中使用。
1. 函数式组件
比如组件About
//App.jsx,在App组件中,其路由嵌套关系为:
<Route path='/about' element={<About />} >
<Route path='/about/singers' element={<Singers />} />
<Route path='/about/platform' element={<Platform/>} />
</Route>
//About.jsx 点击关于歌手按钮,实现页面跳转。
import React from 'react'
import { useNavigate, Outlet } from 'react-router-dom'
function About (props) {
const nav = useNavigate() //调用hook
function navigateTo (path) {
nav(path)
}
return (
<div>
<h2>About</h2>
<button>关于平台</button>
<button onClick={e => navigateTo('/about/singers')}>关于歌手</button>
{/* 占位符,子组件 Singers的内容会展示在这里 */}
<Outlet />
</div>
)
}
export default About
2. 类组件实现手动跳转
类组件要实现手动跳转,需要通过高阶组件给其注入useNavigate()
返回的函数。
// with_router.js
import { useNavigate } from 'react-router-dom'
function withRouter (OriginComponent) {
// useNavigate 只有高阶组件在用,所以只能返回一个函数组件
return function (props) {
const nav = useNavigate()
// nav放进对象里,注入到组件的props里
return <OriginComponent {...props} router={{ nav }} />
}
}
export default withRouter
组件内:
// Home组件,页面的图片在上一节嵌套路由出现过
export class Home extends PureComponent {
navigateTo (path) {
this.props.router.nav(path)
}
render () {
return (
<div>
<h3>Home</h3>
<Link to='/home/recommend'>推荐</Link>
<Link to='/home/rank'>排行</Link>
<button onClick={e => this.navigateTo('/home/songmenu')}>歌曲目录</button>
{/* 占位 */}
<Outlet />
</div>
)
}
}
export default withRouter(Home)
四、路由传参
1. 路径设置占位符(params)
设置占位:
Detail
组件接收这种通过占位符传递的参数,需要用到一个hook函数useParams()
,但它也只能在函数组件中使用。所以我们可以在高阶组件中使用useParams()
,将得到的参数注入到Deatil
的props
中。
还是上面封装的那个高阶组件with_router.js
import { useNavigate, useParams} from 'react-router-dom'
function withRouter (OriginComponent) {
return function (props) {
// 1. 导航
const nav = useNavigate()
// 2. 动态路由参数--params
const params = useParams()
const router = { nav, params, }
return <OriginComponent {...props} router={router} />
}
}
export default withRouter
Detail.jsx
import React, { PureComponent } from 'react'
import withRouter from '../hoc/with_router'
export class Detail extends PureComponent {
render () {
const { params } = this.props.router
return (
<div>
<h2>Detail</h2>
<h3>获取到的id是:{params.id}</h3>
</div>
)
}
}
export default withRouter(Detail)
2. search传递参数
参数通过?拼接到url中(类似query参数)。
与上边类似,可以借助useSearchParams()
获取到路径里拼接的参数。由于这也是一个hook,所以我们也在高阶组件里调用useSearchParams()
,得到参数,注入到组件的props
中。
with_router.js
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
function withRouter (OriginComponent) {
return function (props) {
// 1. 导航
const nav = useNavigate()
// 2. 动态路由参数--params
const params = useParams()
// 3. 字符串参数(query)---
const [searchParams] = useSearchParams()
// console.log(searchParams.get('name'));
const query = Object.fromEntries(searchParams.entries())
const router = { nav, params, query }
return <OriginComponent {...props} router={router} />
}
}
export default withRouter
此处再补充一个hookuseLocation
,也可以获取到字符串拼接的值
const location = useLocation()
console.log('location', location);
Login.jsx
组件接收数据:
export class Login extends PureComponent {
render () {
const { query } = this.props.router
return (
<div>
<h3>登录界面</h3>
<h4>query参数 --{query.name}--{query.age}</h4>
</div>
)
}
}
export default withRouter(Login)
五、路由配置到单独的文件中(useRoutes)
总在App.jsx里写,不直观且代码较为复杂;
因此将路由的映射关系单独配置到到文件router/idnex.js
中
// 写了几个代表性的
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/rank',
element: <HomeRanking />
}
]
},
{
path: '/detail/:id',
element: <Detail />
},
...
{
path: '*',
element: <NotFound />
},
]
export default routes
在App组件中,用到useRoutes
来将这些routes
转换成原来的Routes
这样的标签元素。但useRoutes
是个hook,因此采用高阶组件将其注入到App组件中:
function withRouter (OriginComponent) {
return function (props) {
const routeEle = useRoutes(routes)
return <OriginComponent {...props} routeEle={routeEle} />
}
}
六、路由懒加载
(1)将路径改为懒加载
改为懒加载的路径,会单独打到一个文件里,当用的时候再去加载这个文件。否则会全都打包到一个文件里。
(2)包裹Suspense
标签
import React, { Suspense } from 'react';
...
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<HashRouter>
{/* 包裹Suspense 标签,当路由加载还未显示在页面上时,显示fallback里的内容 */}
<Suspense fallback={<h3>路由懒加载中!!</h3>}>
<App />
</Suspense>
</HashRouter>
);