手动路由的跳转:
上面的跳转,都必须通过点击 link 组件才能实现。
如果想通过 JS 手动跳转,分为两种情况:
- 在函数组件中:React Router 提供了一个 useNavigate Hook,调用
useNavigate()
会返回一个对象,返回的对象是一个函数,调用该函数,并且传入路径即可实现手动跳转。import React from 'react' // 1. 从 react-router-dom 引入 useNavigate 这个 Hook import {useNavigate} from 'react-router-dom' function About() { // 2. 调用 useNavigate,会返回一个对象,返回的对象是一个函数 const navigate = useNavigate() const handleNavToHome = () => { // 3. 调用返回的函数,实现手动跳转。第一个参数是要跳转的路径,第二个参数是非必填的配置项;或者参数也可以传入一个数字,表示前进或者后退 navigate('/home') } return ( <h1 onClick={handleNavToHome}>About</h1> ) } export default About
- 在类组件中:React Router 没有提供在类组件中可以实现手动跳转的 API,可以用原生 JS (例如:
window.location
等)来实现或者通过高阶组件来对类组件进行增强。React Router V5 中本身就是有 withRouter 这个高阶组件的。
// hoc/WithRouter.jsx import {useNavigate} from 'react-router-dom' // 封装一个高阶组件来使类组件中可以获取到 navigate function WithRouter(WrapperComponent) { // 返回一个新的函数组价 function EnhanceComponent(props) { // 在函数组件中调用 useNavigate Hook,将结果作为 props 传入 const navigate = useNavigate() const router = {navigate} return <WrapperComponent {...props} router={router} /> } return EnhanceComponent } export default WithRouter
// Home.jsx import React, {PureComponent} from 'react' import WithRouter from './hoc/WithRouter' class Home extends PureComponent { handleNavToAbout = () => { this.props.router.navigate('/about') } render() { return ( <div> <h1 onClick={this.handleNavToAbout}>Home</h1> </div> ) } } export default WithRouter(Home)
路由参数的传递:
- 通过动态路由的方式。
动态路由指的是路由中的路径并不会固定。
例如:/about
的路径对应一个组件 About。如果在配置 Route 时将 path 写成/about/:userId
,那么路径/about/1
,/about/2
都可以匹配到该 Route,About 组件将都可以显示。这个路由就称为动态路由。
可以使用动态路由来为路由传递参数。// App.jsx import React, {PureComponent} from 'react' import {Routes, Route, Link} from 'react-router-dom' import Home from './components/Home' import About from './components/About' class App extends PureComponent { render() { return ( <div> <div> <Link to='/home'>首页</Link> {/* 1. 传递参数 */} <Link to='/about/123'>关于</Link> </div> <Routes> <Route path='/home' element={<Home/>} /> {/* :userId 匹配动态路由 */} <Route path='/about/:userId' element={<About/>} /> </Routes> </div> ) } } export default App
// About.jsx import React from 'react' import {useParams} from 'react-router-dom' function About() { // 2. 获取参数。调用 useParams Hook 会返回一个对象,返回的对象中就包含动态路由中的参数 const params = useParams() const userId = params.userId return ( <h1>About:{userId}</h1> ) } export default About
- 通过 search 传递参数。
// App.jsx import React, {PureComponent} from 'react' import {Routes, Route, Link} from 'react-router-dom' import Home from './components/Home' import About from './components/About' class App extends PureComponent { render() { return ( <div> <div> <Link to='/home'>首页</Link> {/* 1. 传递参数。将 search 拼接在路径后面 */} <Link to='/about?userId=123'>关于</Link> </div> <Routes> <Route path='/home' element={<Home/>} /> <Route path='/about' element={<About/>} /> </Routes> </div> ) } } export default App
// About.jsx import React from 'react' import {useSearchParams, useLocation} from 'react-router-dom' function About() { // 2. 获取参数。调用 useSearchParams Hook const [searchParams] = useSearchParams() const userId = searchParams.get('userId') // 也可以调用 useLocation Hook,就可以获取到 search,但是是一个字符串,还需要再进行转换 // const location = useLocation() // 通过 location.search 就可以获取到 ?userId=123 return ( <h1>About:{userId}</h1> ) } export default About
路由的嵌套:
路由是可以嵌套的。被嵌套的子路由,配置路径和组件的映射关系需要直接写在父路由下;使用 <Outlet />
组件作为在父路由元素中子路由元素的占位元素,被嵌套的子路由的路径匹配上后,子路由的元素将会显示在 <Outlet />
的位置。
React Router V5 中被嵌套的子路由,配置路径和组件的映射关系直接写在父路由的元素中。
React Router V6 中改为直接放置在父路由下,是为了统一、方便管理。
例如:有一个首页按钮和一个关于按钮,分别可以跳转到 Home 页和 About 页;然后在 Home 页又有一个首页推荐按钮和一个首页排行榜按钮,分别可以跳转到 HomeRecommend 页和 HomeRanking 页。这就是两级的路由嵌套。
// App.jsx
import React, {PureComponent} from 'react'
import {Routes, Route, Link} from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
import HomeRecommend from './components/HomeRecommend'
import HomeRanking from './components/HomeRanking'
class App extends PureComponent {
render() {
return (
<div>
<div>
<Link to='/home'>首页</Link>
<Link to='/about'>关于</Link>
</div>
<Routes>
<Route path='/home' element={<Home/>}>
{/* 1. 被嵌套的子路由,配置路径和组件的映射关系写在父路由下。统一写在此处,方便管理 */}
<Route path='/home/recommend' element={<HomeRecommend />} />
<Route path='/home/ranking' element={<HomeRanking />} />
</Route>
<Route path='/about' element={<About/>} />
</Routes>
</div>
)
}
}
export default App
import React, {PureComponent} from 'react'
import {Link, Outlet} from 'react-router-dom'
class Home extends PureComponent {
render() {
return (
<div>
<h1>Home</h1>
<div>
<Link to='/home/recommend'>首页推荐</Link>
<Link to='/home/ranking'>首页排行榜</Link>
</div>
{/* 2. 子路由元素的占位元素。被嵌套的子路由的路径匹配上后,子路由的元素将会显示在此处 */}
<Outlet />
{/* React Router V5 中被嵌套的子路由,配置路径和组件的映射关系直接写在父路由的元素中 */}
{/* <div>
<Route path='/home/recommend' component={<HomeRecommend />} />
<Route path='/home/ranking' component={<HomeRanking />} />
</div> */}
</div>
)
}
}
export default Home
路由懒加载:
import {Link, Routes, Route} from 'react-router-dom'
// 路由同步加载,打包的时候所有文件将都会被打包到一个 JS 中
import Home from './components/Home'
import About from './components/About'
import NotFound from './components/NotFound'
function App() {
return (
<div>
<div>
<Link to='/home'>首页</Link>
<Link to='/about?userId=123'>关于</Link>
</div>
<Routes>
<Route path='/home' element={<Home />} />
<Route path='/about' element={<About />} />
<Route path='*' element={<NotFound />} />
</Routes>
</div>
)
}
export default App
路由懒加载之后,打包的时候就可以进行分包处理,对其对应的文件进行单独打包,打包到一个单独的 JS 文件中。
React Router 的路由懒加载不是 React Router 提供的功能,而是借助 React 提供的 React.lazy()
来实现。 React.lazy()
需要传入一个函数作为参数,在函数内部可以使用 import()
来异步加载文件。
import()
可以在需要的时候,再加载某个模块;并且 Webpack 检测到import()
,就会对其进行引入的文件进行单独打包。
// index.js
import {Suspense} from 'react'
import ReactDOM from 'react-dom/client'
import {HashRouter} from 'react-router-dom'
import App from './App'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<HashRouter>
{/* 1. 由于路由懒加载,可能会出现某些组件需要加载的时候还没有下载下来的情况,导致报错。因此使用 Suspense 组件包裹根组件,并且传入 fallback。在异步的组件还没下载完成、加载出来的时候,就会先显示 fallback 中传入的内容 */}
<Suspense fallback={<h1>Loading</h1>}>
<App />
</Suspense>
</HashRouter>
)
// App.jsx
import React from 'react'
import {Link, Routes, Route} from 'react-router-dom'
import Home from './components/Home'
// 2. 使用 React.lazy() 实现路由懒加载,在需要的时候才加载组件
const About = React.lazy(() => import('./components/About'))
const NotFound = React.lazy(() => import('./components/NotFound'))
function App() {
return (
<div>
<div>
<Link to='/home'>首页</Link>
<Link to='/about?userId=123'>关于</Link>
</div>
<Routes>
<Route path='/home' element={<Home />} />
<Route path='/about' element={<About />} />
<Route path='*' element={<NotFound />} />
</Routes>
</div>
)
}
export default App
通过配置的方式使用路由:
React Router V5 中想要通过配置的方式使用路由,需要单独安装一个
react-router-config
库。
// config.routers.js
import Home from '../components/Home'
import About from '../components/About'
import HomeRecommend from '../components/HomeRecommend'
// 1. 配置路由
const routers = [
// 一级路由
{
path: '/home',
element: <Home />,
// 通过 children 配置嵌套的二级路由
children: [
{
path: '/home/recommend',
element: <HomeRecommend />,
}
]
},
{
path: '/about',
element: <About />,
},
]
export default routers
// App.jsx
import {Link, useRoutes} from 'react-router-dom'
import routers from './config/routers'
function App() {
return (
<div>
<div>
<Link to='/home'>首页</Link>
<Link to='/about?userId=123'>关于</Link>
</div>
{/* 2. 调用 useRoutes Hook,传入路由的配置信息,渲染出来的其实下面下面注释掉的内容 */}
{useRoutes(routers)}
{/* <Routes>
<Route path='/home' element={<Home />}>
<Route path='/home/recommend' element={<HomeRecommend />} />
</Route>
<Route path='/about' element={<About />} />
</Routes> */}
</div>
)
}
export default App