共计 12926 个字符,预计需要花费 33 分钟才能阅读完成。
一. 简介
- react-router :核心模块,包含 React 路由大部分的核心功能,包括路由匹配算法和大部分核心组件和钩子。
- react-router-dom:React 应用中用于路由的软件包,包括 react-router 的所有内容,并添加了一些特定于 DOM 的 API,包括但不限于 BrowserRouter、HashRouter 和 Link。
- react-router-native: 用于开发 React Native 应用,包括 react-router 的所有内容,并添加了一些特定于 React Native 的 API,包括但不限于 NativeRouter 和 Link。
二. 对比 V5
- 包大小对比
<Route>
特性变更
- path:与当前页面对应的 URL 匹配。
- element:新增,用于决定路由匹配时,渲染哪个组件。代替,v5 的 component 和 render。
<Routes>
代替了<Switch>
<Outlet> </Outlet>
让嵌套路由更简单useNavigate
代替useHistory
- 移除了
<NavLink/>
的activeClassName
和activeStyle
- 钩子
useRoutes
代替react-router-config
- https://reactrouter.com/docs/en/v6/upgrading/v5
三. 用法详解
npm i react-router-dom
1. 一级路由与多级路由
<Routes>
{/*<Route path="/" element={<Film/>}/>*/}
<Route index element={<Film/>}/>
<Route path="/film" element={<Film/>}/>
<Route path="/cinema" element={<Cinema/>}/>
<Route path="/center" element={<Center/>}/>
</Routes>
index 用于嵌套路由,仅匹配父路径时,设置渲染的组件。
解决当嵌套路由有多个子路由但本身无法确认默认渲染哪个子路由的时候,可以增加
index
属性来指定默认路由。index
路由和其他路由不同的地方是它没有 path 属性,他和父路由共享同一个路径。
2. 路由重定向
(1) 官方推荐方案 1: 使用 Navigate 组件替代
import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'
import Film from '../views/Film'
import Cinema from '../views/Cinema'
import Center from '../views/Center'
export default function MyRouter () {
return (
<div>
<Routes>
<Route path="/film" element={<Film />}></Route>
<Route path="/cinema" element={<Cinema />}></Route>
<Route path="/center" element={<Center />}></Route>
<Route path="/" element={<Navigate to='/film' />}></Route>
</Routes>
</div>
)
}
*
万能匹配,当所有都没匹配到就匹配这个*
(2) 官方推荐方案 2: 自定义 Redirect 组件
// components/Redirect.js
import React, { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
export default function Redirect (props) {
const navigate = useNavigate()
useEffect(() => {
navigate(props.to, { replace: true })
}, [])
return null
}
// 其他组件引用
<Route path="/" element={<Redirect to='/film' />}></Route>
(3) 404 如何实现?
import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'
import Redirect from '../components/Redirect'
import NotFound from '../views/NotFound'
export default function MyRouter () {
return (
<div>
<Routes>
<Route path="/film" element={<Film />}></Route>
<Route path="/notfound" element={<NotFound />}></Route>
<Route path="/" element={<Redirect to='/film' />}></Route>
<Route path="*" element={<Navigate to='/notfound' />}></Route>
</Routes>
</div>
)
}
3. 嵌套路由
<Outlet></Outlet>
// router/index.js
import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'
import Film from '../views/Film'
import NowPlaying from '../views//films/NowPlaying'
import ComingSoon from '../views/films/ComingSoon'
import Cinema from '../views/Cinema'
import Center from '../views/Center'
import Search from '../views/Search'
import Redirect from '../components/Redirect'
import NotFound from '../views/NotFound'
export default function MyRouter () {
return (
<div>
<Routes>
<Route path="/film" element={<Film />}>
{/* 支持相对路径和绝对路径 */}
<Route path="" element={<Navigate to='/film/nowplaying' />}></Route>
<Route path="nowplaying" element={<NowPlaying />}></Route>
<Route path="comingsoon" element={<ComingSoon />}></Route>
</Route>
<Route path="/cinema" element={<Cinema />}></Route>
<Route path="/cinema/search" element={<Search />}></Route>
<Route path="/center" element={<Center />}></Route>
<Route path="/notfound" element={<NotFound />}></Route>
<Route path="/" element={<Redirect to='/film' />}></Route>
<Route path="*" element={<Navigate to='/notfound' />}></Route>
</Routes>
</div>
)
}
// views/Film.js
import React from 'react'
import { Outlet } from 'react-router-dom'
export default function Film () {
return (
<div>
Film
<div
style={{ height: 200, backgroundColor: 'skyblue' }}>
轮播
</div>
{/* 路由容器 */}
<Outlet />
</div>
)
}
4. 声明式导航与编程式导航
import React from 'react'
import { NavLink } from 'react-router-dom'
import './TabBar.css'
export default function TbaBar () {
return (
<ul className='tabbar'>
<li>
<NavLink to='/film' className={({ isActive }) => isActive ? 'joe-active' : ''}>Film</NavLink>
</li>
<li>
<NavLink to='cinema' className={({ isActive }) => isActive ? 'joe-active' : ''}>Cinema</NavLink>
</li>
<li>
<NavLink to='center' className={({ isActive }) => isActive ? 'joe-active' : ''}>Center</NavLink>
</li>
</ul >
)
}
// url 传参
const navigate = useNavigate()
navigate(`/detail?id=${id}`)
// 获取 url 参数
import { useSearchParams } from 'react-router-dom'
const [searchParams, setSearchParams] = useSearchParams()
// 获取参数
searchParams.get('id')
// 判断参数是否存在
searchParams.has('id')
// 同时页面内也可以用set方法来改变路由
setSearchParams({"id":2})
// views/NowPlaying.js
import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom';
export default function NowPlaying () {
const navigate = useNavigate()
const [list, setList] = useState([]);
useEffect(() => {
fetch('https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6492296', {
headers: {
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16717661461034803650494465"}',
'X-Host': 'mall.film-ticket.film.list'
}
}).then(res => res.json()).then(res => {
console.log(res.data.films)
setList(res.data.films)
})
}, []);
return (
<div>
NowPlaying
<ul>
{
list.length === 0 ? '加载中...' :
list.map(item => <li key={item.filmId} onClick={() => {
navigate(`/detail?id=${item.filmId}`)
}}>{item.name}</li>)
}
</ul>
</div>
)
}
// views/Detail.js
import React from 'react'
import { useSearchParams } from 'react-router-dom'
export default function Detail (props) {
const [searchParams, setSearchParams] = useSearchParams()
return (
<div>
Detail --- {searchParams.get('id')}
<button onClick={() => {
setSearchParams({ id: 1000 })
}}>猜你喜欢</button>
</div>
)
}
5. 动态路由
// 跳转页面,路由传参
navigate(`/detail/${id}`)
// 配置动态路由
<Route path="/detail/:id" element={<Detail/>}/>
// 获取动态路由参数
const {id} = useParams()
// views/NowPlaying.js
import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom';
export default function NowPlaying () {
const navigate = useNavigate()
const [list, setList] = useState([]);
useEffect(() => {
fetch('https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6492296', {
headers: {
'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"16717661461034803650494465"}',
'X-Host': 'mall.film-ticket.film.list'
}
}).then(res => res.json()).then(res => {
console.log(res.data.films)
setList(res.data.films)
})
}, []);
return (
<div>
NowPlaying
<ul>
{
list.length === 0 ? '加载中...' :
list.map(item => <li key={item.filmId} onClick={() => {
navigate(`/detail/${item.filmId}`)
}}>{item.name}</li>)
}
</ul>
</div>
)
}
// views/Detail.js
import React from 'react'
import { useParams, useNavigate } from 'react-router-dom'
export default function Detail (props) {
const params = useParams()
const navigate = useNavigate()
return (
<div>
Detail --- {params.id}
<button onClick={() => {
navigate(`/detail/${1000}`)
}}>猜你喜欢</button>
</div>
)
}
6. 路由拦截
<Route path="/center" element={
<AuthComponent>
<Center></Center>
</AuthComponent>
}/>
function AuthComponent({children}){
return localStorage.getItem("token") ? children : <Redirect to="/login" />
}
import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'
import Film from '../views/Film'
import NowPlaying from '../views//films/NowPlaying'
import ComingSoon from '../views/films/ComingSoon'
import Cinema from '../views/Cinema'
import Center from '../views/Center'
import Detail from '../views/Detail'
import Search from '../views/Search'
import Login from '../views/Login'
import Redirect from '../components/Redirect'
import NotFound from '../views/NotFound'
export default function MyRouter () {
return (
<div>
<Routes>
<Route path="/film" element={<Film />}>
{/* 支持相对路径和绝对路径 */}
<Route path="" element={<Navigate to='/film/nowplaying' />}></Route>
<Route path="nowplaying" element={<NowPlaying />}></Route>
<Route path="comingsoon" element={<ComingSoon />}></Route>
</Route>
<Route path="/cinema" element={<Cinema />}></Route>
<Route path="/cinema/search" element={<Search />}></Route>
<Route path="/center" element={<AuthComponent><Center /></AuthComponent>}></Route>
<Route path="/detail/:id" element={<Detail />}></Route>
<Route path="/login" element={<Login />}></Route>
<Route path="/notfound" element={<NotFound />}></Route>
<Route path="/" element={<Redirect to='/film' />}></Route>
<Route path="*" element={<Navigate to='/notfound' />}></Route>
</Routes>
</div>
)
}
// 路由拦截组件的封装
function AuthComponent ({ children }) {
return localStorage.getItem("token") ? children : <Redirect to="/login" />
}
7. 路由模式
import {HashRouter} from 'react-router-dom'
import {BrowserRouter} from 'react-router-dom'
import { BrowserRouter } from 'react-router-dom'
import MyRouter from './router';
import TbaBar from './components/TbaBar';
function App () {
return (
<BrowserRouter>
<MyRouter></MyRouter>
<TbaBar></TbaBar>
</BrowserRouter>
);
}
export default App;
如果是
BrowserRouter
模式后端要做资源处理,如果请求的路径是后端处理不了的,就重新渲染前端的index.htlm
页面
8. withRouter
/ 类组件跳转方法
import {
useLocation,
useNavigate,
useParams
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let push = useNavigate();
let params = useParams();
return (
<Component {...props} history={{ location, push, params }} />
);
}
return ComponentWithRouterProp;
}
export default withRouter
// components/withRouter.js
import React from 'react'
import { useLocation, useNavigate, useParams } from "react-router-dom";
export default function withRouter (Component) {
return function (props) {
let location = useLocation()
let push = useNavigate()
let match = useParams()
return <Component {...props} history={{ location, push, match }} />
}
}
// views/films/FilmIem.js
import React, { Component } from 'react'
import withRouter from '../../components/withRouter'
class FilmItem extends Component {
render () {
return (
<li onClick={() => {
this.props.history.push(`/detail/${this.props.filmId}`)
}}>
{this.props.name}
</li>
)
}
}
export default withRouter(FilmItem)
9. 路由懒加载
const LazyLoad = (path) => {
const Comp = React.lazy(() => import(`../views/${path}`))
return (
<React.Suspense fallback={<>加载中...</>}>
<Comp />
</React.Suspense>
)
}
export default function MRouter() {
return (
<Routes>
{/* <Route index element={<Film/>}/> */}
<Route path="/film" element={LazyLoad("Film")}>
{/* <Route index element={<Nowplaying/>}/> */}
<Route path="" element={<Redirect to="/film/nowplaying"/>}/>
<Route path="nowplaying" element={LazyLoad("film/Nowplaying")}/>
<Route path="comingsoon" element={LazyLoad("film/Comingsoon")}/>
</Route>
<Route path="/cinema" element={LazyLoad("Cinema")}/>
<Route path="/login" element={LazyLoad("Login")}/>
<Route path="/center" element={<AuthComponent>
{LazyLoad("Center")}
</AuthComponent>}/>
<Route path="/detail/:id" element={LazyLoad("Detail")}/>
<Route path="/" element={<Redirect to="/film"/>}/>
<Route path="*" element={LazyLoad("NotFound")}/>
</Routes>
)
}
// router/index.js
import React from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'
import Redirect from '../components/Redirect'
export default function MyRouter () {
return (
<div>
<Routes>
<Route path="/film" element={lazyLoad('Film')}>
{/* 支持相对路径和绝对路径 */}
<Route path="" element={<Navigate to='/film/nowplaying' />}></Route>
<Route path="nowplaying" element={lazyLoad('films/NowPlaying')}></Route>
<Route path="comingsoon" element={lazyLoad('films/ComingSoon')}></Route>
</Route>
<Route path="/cinema" element={lazyLoad('Cinema')}></Route>
<Route path="/cinema/search" element={lazyLoad('Search')}></Route>
<Route path="/center" element={<AuthComponent>{lazyLoad('Center')}</AuthComponent>}></Route>
{/* 动态路由 */}
<Route path="/detail/:id" element={lazyLoad('Detail')}></Route>
<Route path="/login" element={lazyLoad('Login')}></Route>
<Route path="/notfound" element={lazyLoad('NotFound')}></Route>
<Route path="/" element={<Redirect to='/film' />}></Route>
<Route path="*" element={<Navigate to='/notfound' />}></Route>
</Routes>
</div>
)
}
// 路由拦截组件的封装
function AuthComponent ({ children }) {
return localStorage.getItem("token") ? children : <Redirect to="/login" />
}
// 路由懒加载的封装
function lazyLoad (path) {
const Com = React.lazy(() => import(`../views/${path}`))
return (
<React.Suspense fallback={<>加载中...</>}>
<Com />
</React.Suspense>
)
}
10. useRoutes
钩子配置路由(路由表)
// router/index.js
import React from 'react'
import { useRoutes, Navigate } from 'react-router-dom'
import Redirect from '../components/Redirect'
export default function MyRouter () {
const element = useRoutes([
{
path: '/film', element: lazyLoad('Film'), children: [
{ path: '', element: <Navigate to='/film/nowplaying' /> },
{ path: 'nowplaying', element: lazyLoad('films/NowPlaying') },
{ path: 'comingsoon', element: lazyLoad('films/ComingSoon') },
]
},
{ path: '/cinema', element: lazyLoad('Cinema') },
{ path: '/center', element: <AuthComponent>{lazyLoad('Center')}</AuthComponent> },
{ path: '/detail/:id', element: lazyLoad('Detail') },
{ path: '/login', element: lazyLoad('Login') },
{ path: '/notfound', element: lazyLoad('NotFound') },
{ path: '/', element: <Redirect to='/film' /> },
{ path: '*', element: <Navigate to='/notfound' /> },
])
return element
}
// 路由拦截组件的封装
function AuthComponent ({ children }) {
return localStorage.getItem("token") ? children : <Redirect to="/login" />
}
// 路由懒加载的封装
function lazyLoad (path) {
const Com = React.lazy(() => import(`../views/${path}`))
return (
<React.Suspense fallback={<>加载中...</>}>
<Com />
</React.Suspense>
)
}
export default function MRouter() {
const element = useRoutes([
{path:"/film",element:LazyLoad("Film"),children:[
{
path:"",
element:<Redirect to="/film/nowplaying"/>
},
{
path:"nowplaying",
element:LazyLoad("film/Nowplaying")
},
{
path:"comingsoon",
element:LazyLoad("film/Comingsoon")
}
]},
{
path:"/cinema",element:LazyLoad("Cinema")
},
{
path:"/login",element:LazyLoad("Login")
},
{
path:"/center",element:<AuthComponent>
{LazyLoad("Center")}
</AuthComponent>
},
{
path:"/detail/:id",element:LazyLoad("Detail")
},
{
path:"/",element:<Redirect to="/film"/>
},
{
path:"*",element:LazyLoad("NotFound")
},
])
return element
}
// src/App.js
import { BrowserRouter } from 'react-router-dom'
import MyRouter from './router';
function App () {
return (
<BrowserRouter>
<MyRouter></MyRouter>
</BrowserRouter>
);
}
export default App;
正文完