React
路由导航
Router
- react-router
- 核心模块
- 包含React路由的大部分核心功能、路由匹配算法、核心组件、钩子
- react-router-dom
- React应用中用于软件包
- 包括react-router的全部内容
- 并添加了一些特定于DOM的API
- react-rouer-native
- 用于开发React-Native应用
- 包括react-router的所有内容
- 并添加了一些特定于React-Native的API
代码示例:
/* src/css/Tabber.css */
.myActive {
color: #30363d;
}
// src/components/Redirect.jsx
// 自定义
import {useEffect} from "react";
import {useNavigate} from "react-router-dom";
export default function Redirect({to}) { // 直接解构
const navigate = useNavigate()
useEffect(() => {
navigate(to, {replace: true}) // replace:是否关掉上一个页面,重新打开指定页面
})
return null
}
// src/components/Tabber.jsx
// NavLink:具备Link的所有功能,相比Link增加了高亮显示
import React from "react";
import {NavLink} from "react-router-dom";
import '../css/Tabber.css'
export default function Tabber() {
return (
<footer>
<ul>
<li> {/* 设置key与否取决于是否有虚拟DOM比对 */}
<NavLink to={'/films'} className={({isActive}) => // 直接解构
isActive ? 'myActive' : '' // 取决于isActive是否为真(属性被添加到此DOM中)
}>首页
</NavLink>
</li>
<li>
<NavLink to={'/cinemas'} className={({isActive}) =>
isActive ? 'myActive' : ''
}>内容
</NavLink>
</li>
<li>
<NavLink to={'/centers'} className={({isActive}) =>
isActive ? 'myActive' : ''
}>我的
</NavLink>
</li>
</ul>
</footer>
)
}
// src/components/WithRouter.jsx
/*
import React from "react";
import {useNavigate, useParams, useLocation} from "react-router-dom";
// 自行封装WithRouter(应用于类组件兼容,高阶组件)
export default function WithRouter(Component) {
return function (props) {
const push = useNavigate()
const match = useParams()
const location = useLocation()
return <Component {...props} history={{push, match, location}}/>
}
}
*/
import React from "react";
export default function WithRouter() {
return (
<div>
FilmItems_class
</div>
)
}
// src/router/index.jsx
import React from "react";
import {Route, Routes} from "react-router-dom";
// import Films from "../views/Films";
// import Centers from "../views/Centers";
// import Cinemas from "../views/Cinemas";
import Redirect from '../components/Redirect'
// import NotFound from '../views/NotFound'
// import Nowplaying from "../views/films/Nowplaying";
// import Comingsoon from "../views/films/Comingsoon";
// import Detail from "../views/Detail";
// import Login from "../views/Login";
import AuthComponent from "../views/AuthComponent";
const LazyLoad = (path) => { // 自行封装路由懒加载(避免首次加载过慢)
const Comp = React.lazy(() => import(`../${path}`))
return (
<React.Suspense fallback={<>加载ing...</>}>
<Comp/>
</React.Suspense>
)
}
export default function MRouter() {
// function isAuth() {
// return localStorage.getItem('token') // 获取浏览器本地缓存的token键值(演示)
// }
return (
<Routes>
{/* <Route index element={<Films/>}/> index:指定默认子路由(以父路径为准) */}
<Route path={'/films'} element={LazyLoad('views/Films')}>
{/* <Route index element={<Nowplaying/>}/> 默认子路由'/films/'(嵌套在父组件中,也等同于'') */}
<Route path={''} element={<Redirect to={'/films/nowplaying'}/>}/>
{/* 省略'/films/':相对于父路径 */}
<Route path={'nowplaying'} element={LazyLoad('views/films/Nowplaying')}/>
<Route path={'comingsoon'} element={LazyLoad('views/films/Comingsoon')}/>
</Route>
<Route path={'/centers'} element={<AuthComponent>{LazyLoad('views/Centers')}</AuthComponent>}/>
<Route path={'/login'} element={LazyLoad('views/Login')}/>
<Route path={'/detail/:id'} element={LazyLoad('views/Detail')}/> {/* 动态路由 */}
<Route path={'/cinemas'} element={LazyLoad('views/Cinemas')}/>
<Route path={'/'} element={<Redirect to={'/films'}/>}/>
<Route path={'*'} element={LazyLoad('views/NotFound')}/>
{/* <Route path={'/'} element={<Navigate to={'/films'}/>}/> 匹配'/'时重定向到'/films' */}
{/* <Route path={'*'} element={<Navigate to={'/films'}/>}/> '*':万能匹配(如果前面的路由都没匹配上的话) */}
</Routes>
)
}
// src/router/index_useRoutes.jsx
import React from "react";
import {useRoutes} from "react-router-dom";
import Redirect from '../components/Redirect'
import AuthComponent from "../views/AuthComponent";
const LazyLoad = (path) => {
const Comp = React.lazy(() => import(`../${path}`))
return (
<React.Suspense fallback={<>加载ing...</>}>
<Comp/>
</React.Suspense>
)
}
export default function MRouter() {
const element = useRoutes([
{
path: '/films',
element: LazyLoad('views/Films'),
children: [
{
path: '',
element: <Redirect to={'/films/nowplaying'}/>
},
{
path: 'nowplaying',
element: LazyLoad('views/films/Nowplaying')
},
{
path: 'comingsoon',
element: LazyLoad('views/films/Comingsoon')
}
]
},
{
path: '/centers',
element:
<AuthComponent>
{LazyLoad('views/Centers')}
</AuthComponent>
},
{
path: '/login',
element: LazyLoad('views/Login')
},
{
path: '/cinemas',
element: LazyLoad('views/Cinemas')
},
{
path: '/detail/:id',
element: LazyLoad('views/Detail')
},
{
path: '/',
element: <Redirect to={'/films'}/>
},
{
path: '*',
element: LazyLoad('views/NotFound')
}
])
return (
element
)
}
// src/views/films/Comingsoon.jsx
import React from "react";
export default function Comingsoon() {
return (
<div>
Comingsoon
</div>
)
}
// src/films/FilmItems.jsx
import React from "react";
import {useNavigate} from "react-router-dom";
export default function FilmItems(item) { // props
const navigate = useNavigate() // 可以在任何路由包裹的组件内部使用
const handleChangePage = (Id) => { // 编程式导航
// 方式01:query(URLSearch)传参,/detail?id=1024
// 方式02:路由传参(/detail/1024),navigate(`/detail?id=${Id}`)
navigate(`/detail/${Id}`)
}
return (
<li onClick={() => handleChangePage(item.filmId)}>
{item.name}
</li>
)
}
// src/FilmItems_class.jsx
/*
import React, {Component} from "react";
import WithRouter from "../../components/WithRouter";
class FilmItems_class extends Component {
handleClick(id) {
console.log(this.props.history)
this.props.history.push(`/detail/${id}`) // 跳转页面
this.props.history.match() // 获取参数
this.props.history.location() // 获取当前路由
}
render() {
return (
<li onClick={() => this.handleClick(this.props.filmId)}>
{
this.props.name
}
</li>
)
}
}
export default WithRouter(FilmItems_class)
*/
import React from "react";
export default function FilmItems_class() {
return (
<div>
FilmItems_class
</div>
)
}
// src/views/films/Nowplaying.jsx
import {useEffect, useState} from "react";
import axios from "axios";
import FilmItems from "./FilmItems";
export default function Nowplaying() {
const [list, setList] = useState([])
useEffect(() => {
axios.get(`/test.json`).then((res) => {
console.log(res.data.films)
setList(res.data.films)
}).catch(err => console.log(err))
}, [])
return (
<div>
<ul>
{
list.map(item =>
<FilmItems key={item.filmId} {...item}/>
)
}
</ul>
</div>
)
}
// src/views/AuthComponent.jsx
import React from "react";
import Redirect from "../components/Redirect";
// 自定义路由拦截组件
export default function AuthComponent({children}) { // 接收的是子组件
// 演示
const isLogin = localStorage.getItem('token')
return isLogin ? children : <Redirect to={'/login'}/>
}
// src/views/Centers.jsx
import React from "react";
export default function Centers() {
return (
<div>
Center
</div>
)
}
// src/views/Cinemas.jsx
import React from "react";
export default function Cinemas() {
return (
<div>
Cinema
</div>
)
}
// src/views/Detail.jsx
import React from "react";
import {useNavigate, useParams} from "react-router-dom";
export default function Detail() {
const params = useParams()
console.log(params) // 拿到一个对象(键:设置的占位符对应的名称)
const navigate = useNavigate()
return (
<div>
<button onClick={() => {
navigate(`/detail/${1024}`) // 演示
}}>随机页面</button>
</div>
)
}
// src/views/Detail_Search.jsx
// window.location.href(获取当前页面的完整路径)
import React from "react";
import {useSearchParams} from "react-router-dom";
export default function Detail_Search() {
const [searchParams,setSearchParams] = useSearchParams()
console.log(searchParams.get('id')) // 获取参数键值
/*
console.log(searchParams.has('id')) // 判断参数是否存在
useEffect(() => {
setSearchParams({'id': 1024}) // 改变路由
},[setSearchParams])
*/
return (
<div>
<button onClick={() => {
setSearchParams({id: 1024}) // 演示
}}>随机页面</button>
</div>
)
}
// src/views/Films.jsx
import React from "react";
import {Outlet} from "react-router-dom";
export default function Films() {
return (
<div>
<div style={{height: '200px', background: 'gray'}}>轮播</div>
<Outlet/> {/* 嵌套路由容器 */}
</div>
)
}
// src/views/Login.jsx
import React, {useEffect} from "react";
import {useNavigate} from "react-router-dom";
export default function Login() {
const navigate = useNavigate()
useEffect(() => {
localStorage.getItem('token') && navigate('/centers')
}, [navigate])
return (
<div>
{/* 演示 */}
<input type="text"/>
<input type="password"/>
<button onClick={() => {
localStorage.setItem('token', 'HQSY')
navigate('/centers')
}}>登录
</button>
</div>
)
}
// src/views/NotFound.jsx
import React from "react";
export default function NotFound() {
return (
<div>
<h1>404</h1>
</div>
)
}
// src/App.jsx
import React from "react";
import {BrowserRouter} from "react-router-dom";
import MRouter from "./router";
import Tabber from "./components/Tabber";
/*
1. HashRouter(带'#')
2. BrowserRouter(不带'#')
3. 区别
· HashRouter不会与后端路由冲突(后端识别不了'#')
· BrowserRouter会与后端路由冲突(因为会先请求后端的路由,如果后端处理不了对应的路径,则会返回404状态码)
4. 解决
· 路由功能全部交给前端或后端其中一个
· 后端处理不了的路径移交给前端路由
*/
export default function App() {
return (
<BrowserRouter>
<MRouter/>
<Tabber/>
</BrowserRouter>
)
}
// src/index.jsx
import React from "react";
import {createRoot} from "react-dom/client";
import App from "./App";
createRoot(document.getElementById('root')).render(<App/>)