465 lines
9.2 KiB
Markdown
Executable File
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>
|
|
);
|
|
```
|
|
|