文章目录
- 一、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.statereturn (<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() //调用hookfunction 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. 动态路由参数--paramsconst 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.routerreturn (<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. 动态路由参数--paramsconst 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.routerreturn (<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>
);