devStandard/docs/learning/7-react/2-React Router(V5).md
2025-03-29 14:35:49 +08:00

465 lines
9.2 KiB
Markdown
Executable File

# React Router(V5)
> 将url地址和组件进行映射
[React Router: Declarative Routing for React.js](https://v5.reactrouter.com/web/guides/quick-start)
#### 安装
```bash
# 安装v5
pnpm add react-router-dom@5
```
## helloRouter
```jsx
/**
* react router 使用步骤
* 1. 安装react-router-dom包
* 2. 在main.js 中引入BrowserRouter组件
* 3. 将BrowserRouter设置为根组件
*/
```
```jsx
// @/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import {BrowserRouter as Router} from "react-router-dom";
ReactDOM.createRoot(document.getElementById('root')).render(
<Router>
<App />
</Router>
)
```
```jsx
/**
* 1. 引入Route组件
* 2. 设置 path 和 component属性, 访问对应路径,就会挂载对应组件
* 注意: 默认情况下Route并不是严格匹配
* 举例: 访问'/about',除了About组件,还会挂载Home组件
* 访问'/about/123',会挂载Home和About组件
* 所以要设置exact, 严格匹配
*/
```
```jsx
// @/App.jsx
import { Route } from 'react-router-dom/cjs/react-router-dom.min'
import Home from './components/Home'
import About from './components/About'
const App = ()=>{
return (
<div className="App">
<Route exact path='/' component={Home}/>
<Route exact path='/about' component={About}/>
</div>
)
}
export default App
```
## 路由跳转 Link / NavLink
> 使用react router时, 不能使用a标签超链接切换路径, 因为会重新加载页面, 向服务器重新发请求
>
>使用 Link组件或者NavLink组件
```jsx
//@/components/Menu.jsx
import { Link } from 'react-router-dom'
const Menu = () => {
return (
<ul>
<li>
<Link to="/">主页</Link>
</li>
<li>
<Link to="/about">关于</Link>
</li>
</ul>
)
}
export default Menu
```
```jsx
/**
* NavLink和Link类似,但可以指定激活路径的链接样式
* 同样需要设置严格模式 exact
* activeClassName 为激活的链接添加类名
* activeStyle 为激活的标签添加样式
*/
```
```jsx
//@/components/Menu.jsx
import { NavLink } from 'react-router-dom'
import classes from './Menu.module.css'
const Menu = () => {
return (
<ul>
<li>
<NavLink exact activeClassName={classes.active} to="/">主页</NavLink>
</li>
<li>
<NavLink exact activeStyle={{color:'green'}} to="/about">关于</NavLink>
</li>
</ul>
)
}
export default Menu
```
```css
/* @/src/components/Menu.module.css */
a:link,
a:visited {
color: black;
text-decoration:none;
}
a:hover {
color: skyblue;
}
a.active {
color:green;
}
```
## 两种路由模式 BrowserRouter HashRouter
```jsx
/**
* 1. 地址栏: hash多了个#号, http://127.0.0.1:5173/#/ http://127.0.0.1:5173/#/about
* 2. 开发过程: 没有区别
* 3. 部署过程:
* BrowserRouter是直接通过url地址进行跳转
* 直接通过链接进行跳转,没有经过服务器,没有问题
* 直接在地址栏输入或者刷新,会向服务器发送请求,404
* 解决方案: 1. 换成HashRouter 2.需要修改服务器,将所有请求都转发到index.html
* HashRouter是url中的hash来对地址进行匹配, 服务器不会判断hash
*/
```
```conf
# 修改nginx配置
server {
location / {
root html;
#index index.html;
try_files $url /index.html;
}
}
```
## 路由传参
### component挂载组件
```js
/**
* component直接传组件的类,会自动创建组件并传递参数
* match: 匹配的信息
* isExact 检查路径是否完全匹配
* params 请求的参数 <Route path='/student/:id' component={student} />
* location: 地址信息
* pathname 当前路径
* search 搜索参数
* history: 历史记录的信息, 主要用来控制页面的跳转
* goForward() 前进
* goBack() 后退
* go(n) 参数为数字
* push() 跳转,有历史记录, 需要一个location作为参数
* replace() 替换,没有历史记录
*/
```
```jsx
//@/components/Student.jsx
const Student = (props)=>{
console.log(props)
const clickHandler = ()=>{
props.history.push({
pathname:'/student/2',
state: {
// 在props.location.state可以找到
name: 'test'
}
})
}
const clickHandler2 = ()=>{
props.history.replace({
pathname:'/student/3'
})
}
return (
<div>
<p>{JSON.stringify(props.match.params)}</p>
<p>{JSON.stringify(props.location.state)}</p>
<button onClick={clickHandler}>用push方法跳转学生2</button>
<br/>
<button onClick={clickHandler2}>用replace方法跳转学生3</button>
</div>
)
}
export default Student
```
```jsx
// src/App.jsx
import { Route } from 'react-router-dom/cjs/react-router-dom.min'
import Home from './components/Home'
import About from './components/About'
import Menu from './components/Menu'
import Student from './components/Student'
const App = ()=>{
return (
<div className="App">
<Menu />
<hr />
<Route exact path='/' component={Home}/>
<Route exact path='/about' component={About}/>
<Route path='/student/:id' component={Student} />
</div>
)
}
export default App
```
```jsx
// src/components/Menu.jsx
import { NavLink } from 'react-router-dom'
import classes from './Menu.module.css'
const Menu = () => {
return (
<ul>
<li>
<NavLink exact activeClassName={classes.active} to="/">主页</NavLink>
</li>
<li>
<NavLink exact activeStyle={{color:'green'}} to="/student/1">学生1</NavLink>
</li>
<li>
<NavLink exact activeStyle={{color:'green'}} to="/about">关于</NavLink>
</li>
</ul>
)
}
export default Menu
```
### render挂载组件
```js
// 这样挂载组件有缺点, 传入的是类,没办法自定义属性或者传递参数
// 解决方法 用render或者children
<Route path='/student/:id' component={Student} />
```
```js
<Route exact path='/student/:id' render={(routerProps)=>{
console.log(routerProps)
return <Student {...routerProps} />
}}/>
```
### children挂载组件
```js
// src/App.jsx
<Route path='/student/:id' children={<Student />} />
// 或者 <Route path='/student/:id' ><Student /></Route>
```
```jsx
// src/components/Student.jsx
import { useHistory, useRouteMatch,useLocation,useParams } from 'react-router-dom'
const Student = ()=>{
/**
* 用钩子函数获取路由
*/
const match = useRouteMatch()
const location = useLocation()
const history = useHistory()
const params = useParams()
const clickHandler = ()=>{
history.push({
pathname:'/student/2',
state: {
// 在props.location.state可以找到
name: 'test'
}
})
}
const clickHandler2 = ()=>{
history.replace({
pathname:'/student/3'
})
}
return (
<div>
<p>{JSON.stringify(params)}</p>
<p>{JSON.stringify(location.state)}</p>
<button onClick={clickHandler}>用push方法跳转学生2</button>
<br/>
<button onClick={clickHandler2}>用replace方法跳转学生3</button>
</div>
)
}
export default Student
```
## 路由嵌套
> 访问/about显示About组件, 访问/about/hello, 既显示About组件, 也显示hello组件
```jsx
// src/App.jsx
// ...
// exact 去掉
<Route path='/about'>
<About />
<Route path='/about/hello'>
<Hello />
</Route>
</Route>
//...
```
或者在`<About>`组件中添加
```jsx
// src/components/About.jsx
// ...
// exact 去掉
<Route path='/about/hello'>
<Hello />
</Route>
// ...
```
为了避免要手动补全路径, 使用钩子获取当前组件path
```jsx
// src/components/About.jsx
import {useRouteMatch,Route} from 'react-router-dom'
import Hello from './Hello'
const About = () => {
const {path} = useRouteMatch()
return (
<div>
<p>about页面</p>
<Route path={`${path}/hello`} >
<Hello />
</Route>
</div>
)
}
export default About
```
## prompt提示组件
> 离开当前路由时提示
```jsx
import { useState } from 'react'
import { Prompt } from 'react-router-dom'
const Form = ()=>{
const [input, setInput] = useState('')
const [showPrompt, setShowPrompt] = useState(false)
const changeHandler = (e)=>{
const format = e.target.value.trim()
setInput(format)
setShowPrompt(!!format)
}
return (
<div>
<h3>表单</h3>
<Prompt
when={showPrompt} {}
message={'将要离开页面,数据不会保留!确定吗?'}
/>
<input type="text" value={input} onChange={changeHandler}/>
</div>
)
}
export default Form
```
## Redirect重定向组件
```js
/**
* Redirect 重定向组件
* to 跳转的url,可以是string,也可以是location
* 默认是通过replace跳转
* 添加push属性,改为push跳转
*/
```
```jsx
<Route exact path="/">
{loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>
```
```jsx
<Redirect push to="/somewhere/else" />
```
## Switch组件
> 常用于权限判断路由权限
`Redirect`一起用, `Redirect` 要添加from
```jsx
import { Redirect, Route, Switch } from "react-router";
let routes = (
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/users">
<Users />
</Route>
<Redirect from="/accounts" to="/users" />
<Route>
<NoMatch />
</Route>
</Switch>
);
```