React 路由【第五章】

相关理解

  • SPA:单页面应用
    • 整个应用只有一个完整的页面
    • 点击页面链接不会刷新页面,只会页面局部更新
    • 数据通过ajax请求获取,在前端异步展现
  • 路由【一个路由就是一个映射关系(key:value)key:路径 value:function或者component】
    • 后端路由【value是function,处理客户端提交的请求】
      1. 注册路由:router.get(path,function(req,res))
      2. 工作流程:当node接收到请求,根据请求路径找到匹配的路由,调用路由中的函数处理请求,返回请求数据
    • 前端路由【浏览器路由,values是component,用于展示页面内容】
      1. 注册路由 <Route path="/test" component={Test}>
      2. 工作过程:当浏览器的path变成/test,前端路由组件就会变成Test组件
  • react-router
    1. react 的一个插件库
    2. 专门实现SPA应用
    3. 基于react 项目基本都会用到此库

react-router相关API

  • 内置组件
    1. <BrowserRouter>
    2. <HashRouter>
    3. <Route>
    4. <Redirect> ——————react-router-dom@6取消了重定向方法
    5. <link>
    6. <NavLink>
    7. <Switch> ——————<Switch>重命名为<Routes>
  • 其他
    1. history对象
    2. match对象
    3. withRouter对象
  • 注意事项
    • Route 组件必须包裹 Routes 组件
    • Route 组件属性由原来的 component 改为了 element
    • a标签由 Link组件代替 to跳转路径
      • <Link className="list-group-item" to="/about">About</Link>
    • 渲染的时候 App组件 包裹 BrowserRouter 或者 HashRouter
      • ReactDOM.render(<BrowserRouter><App/></BrowserRouter> ,document.getElementById('root'));
    • Navlink组件 改样式【v6版 支持小写 activeclassname=’active’】【v5 activeClassName】
      • <NavLink className={({ isActive }) => "list-group-item" + (isActive ? " active1" : "")} to="/about">About</NavLink>
    • 标签体内容是一个特殊的标签属性【this.props.children获取】 【对NavLink 封装到 MyNavLink组件】
      • <MyNavLink name='About' to="/about" a={1} a={100} a='12'>About</MyNavLink>
        1
        2
        3
        4
        5
        // v6
        <Routes>
        <Route path="/about" element={<About></About>}></Route>
        <Route path="/home" element={<Home></Home>}></Route>
        </Routes>
  • 路由组件和一般组件
    1. 存放位置:一般组件【components】路由组件【pages】
    2. 写法:一般组件<demo/> 路由组件 <Route path="/home" element={<Home></Home>}></Route>
    3. 接收到的props不同【v5版】: 一般组件【写什么接收什么】 路由组件【v5:接收三个固定属性:history、localtion、match】
  • Switch组件
    1. 通常情况下,path和component是一一对应的关系。
    2. Switch可以提高路由匹配效率(单一匹配)。
      1
      2
      3
      4
      5
      6
      // v5
      <Switch>
      <Route path="/home" component={Home}></Route>
      <Route path="/about" component={About}></Route>
      <Route path="/about" component={Test}></Route>
      </Switch>
  • v5 : 精准匹配 与 模糊匹配 【 开启精准匹配: exact={true}】
    • <Route exact={true} path="/about" element={<About></About>}></Route>
  • v5 : Redirect 重定向 v6 :Navigate useNavigate
    • //v5 <Redirect to="/about"></Redirect>
    • //v6 <Route path="*" element={<Navigate to="/about"/>} />

嵌套路由【v6】

1
2
3
4
5
6
7
8
9
10
11
12
// App.jsx
Routes>
<Route path="/about" element={<About></About>}></Route>
<Route path="/home" element={<Home></Home>}>
<Route path="/home/news" element={<News></News>}></Route>
<Route path="/home/messages" element={<Messages></Messages>}></Route>
<Route path="/home" element={<Navigate to="/home/news" />} />
</Route>
<Route path="*" element={<Navigate to="/about" />} />
</Routes>
// Home.jsx
<Outlet></Outlet>

路由传值的三种方式(v5 和 v6)

1.params参数【传参带/,路由带冒号,useParams】

v5:

1
2
3
4
5
6
7
8
9
//路由链接(携带参数):
<Link to='/demo/test/tom/18'}>详情</Link>
//或 <Link to={{ pathname:'/demo/test/tom/18' }}>详情</Link>

//注册路由(声明接收):
<Route path="/demo/test/:name/:age" component={Test}/>

//接收参数:
this.props.match.params

v6: useParams 只能在函数组件中使用
<Link to={/home/messages/detail/${id}/${title}}>Child1</Link>
<Route path="/home/messages/detail:id/:title" component={Test}/>

1
2
3
4
5
6
7
8
9
10
11
//路由链接(携带参数):
<Link to={{ pathname:`/b/child1/${id}/${title}` }}>Child1</Link>
//或 <Link to={`/b/child1/${id}/${title}`}>Child1</Link>

//注册路由(声明接收):
<Route path="/b/child1/:id/:title" component={Test}/>

//接收参数:
import { useParams } from "react-router-dom";
const params = useParams();
//params参数 => {id: "01", title: "消息1"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { Component } from 'react'
import { useParams } from 'react-router-dom'
export default function Detail() {
const {id,title} = useParams();
return (
<div>
<ul>
<li>ID: {id}</li>
<li>TITLE:{title}</li>
<li>CONTENT:???</li>
</ul>
</div>
)
}

2.search参数【传参带问号,路由不变,useSearchParams】

v5

1
2
3
4
5
6
7
8
9
10
//路由链接(携带参数):
<Link to='/demo/test?name=tom&age=18'}>详情</Link>

//注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/>

//接收参数:
this.props.location.search

//备注:获取到的search是urlencoded编码字符串(例如: ?id=10&name=zhangsan),需要借助query-string解析参数成对象

v6: useSearchParams 只能在函数组件中使用
<Link to={/home/messages/detail?id=${item.id}&title=${item.title}}>{item.title}</Link>
<Route path="/home/messages/detail" element={<Detail></Detail>}></Route>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//路由链接(携带参数):
<Link className="nav" to={`/b/child2?age=20&name=zhangsan`}>Child2</Link>

//注册路由(无需声明,正常注册即可):
<Route path="/b/child2" component={Test}/>

//接收参数方法1:
import { useLocation } from "react-router-dom";
import qs from "query-string";
const { search } = useLocation();
//search参数 => {age: "20", name: "zhangsan"}

//接收参数方法2:
import { useSearchParams } from "react-router-dom";
const [searchParams, setSearchParams] = useSearchParams();
// console.log( searchParams.get("id")); // 12

//备注:获取到的search是urlencoded编码字符串(例如: ?age=20&name=zhangsan),需要借助query-string解析参数成对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React, { Component } from 'react'
// 方式一
import { useSearchParams } from "react-router-dom";
// 方式二
// import { useLocation } from "react-router-dom";
// import qs from "query-string";

export default function Detail() {
// 1.接收参数
const [searchParams, setSearchParams] = useSearchParams();
// console.log(searchParams.get("id"));
// console.log(searchParams.get("title"));
// 2.接收参数
// const { search } = useLocation();
// let { id, title } = qs.parse(search)
return (
<div>
<ul>
<li>ID: {searchParams.get("id")}</li>
<li>TITLE: {searchParams.get("title")}</li>
{/* <li>ID: {id}</li>
<li>TITLE: {title}</li> */}
<li>CONTENT:???</li>
</ul>
</div>
)
}

3.state参数【传参带state属性,路由不变,useLocation,[浏览器路径:/home/messages/detail,没有带参数]】

v5

1
2
3
4
5
6
7
8
9
10
//路由链接(携带参数):
<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>

//注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/>

//接收参数:
this.props.location.state

//备注:刷新也可以保留住参数

v6
<Link to={/home/messages/detail} state={{ id: item.id, title: item.title }} >{item.title}</Link>
<Route path="/home/messages/detail" element={<Detail></Detail>}></Route>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//通过Link的state属性传递参数
<Link
className="nav"
to={`/b/child2`}
state={{ id: 999, name: "i love merlin" }}
>
Child2
</Link>

//注册路由(无需声明,正常注册即可):
<Route path="/b/child2" component={Test}/>

//接收参数:
import { useLocation } from "react-router-dom";
const { state } = useLocation();
//state参数 => {id: 999, name: "我是梅琳"}

//备注:刷新也可以保留住参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { Component } from 'react'
import { useLocation } from "react-router-dom";
{/* <Route path="/home/messages/detail" element={<Detail></Detail>}></Route> */}
{/* <Link to={`/home/messages/detail`} state={{ id: item.id, title: item.title }} >{item.title}</Link> */}

export default function Detail(){
const { state } = useLocation();
console.log(useLocation());
return (
<div>
<ul>
<li>ID: {state.id}</li>
<li>TITLE: {state.title}</li>
<li>CONTENT:???</li>
</ul>
</div>
)
}

class组件接收参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import React, { Component, useImperativeHandle } from 'react'
import { useParams } from "react-router-dom";
// APP.jsx 路由配置
{/* <Route path="/home/messages/detail/:id/:title" element={<Detail></Detail>}></Route> */}
// Messages.jsx 列表页面
{/* <Link to={`/home/messages/detail/${item.id}/${item.title}`}>{item.title}</Link> */}

function A(props) {
// 获取动态路由的值
const params = useParams();
useImperativeHandle(props.onRef, () => {
return {
params,
}
});
return (
<span></span>
)
}

export default class Detail extends Component {
constructor(props) {
super(props);
this.state = {
// 创建ref
funRef: React.createRef(),
data:{}
}
}
paramsData = ()=> {
let {funRef} = this.state;
this.setState({
data:funRef.current.params
})
console.log(funRef.current.params);
}
render() {
let {funRef,data} = this.state;
return (
<div>
<ul>
<li>ID: {data.id}</li>
<li>TITLE: {data.title}</li>
<li>CONTENT:???</li>
</ul>
<button onClick={this.paramsData}>获取params数据</button>
<A onRef={funRef} style={{ display: 'none' }}></A>
</div>
)
}
}

push 和 replace 模式

react 默认是push模式

  • replace跳转不会形成history,不可返回到上一层
    this.props.history.replace(‘router地址’)
  • push跳转会形成history,可返回到上一层
    this.props.history.push(‘router地址’)

编程式跳转

函数式组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// v5
import { useHistory } from 'react-router-dom';

function MyButton() {
let history = useHistory();
function handleClick() {
history.push('/home');
};
return <button onClick={handleClick}>Submit</button>;
};

// v6
import { useNavigate } from 'react-router-dom';

function MyButton() {
let navigate = useNavigate();
function handleClick() {
navigate('/home');
};
return <button onClick={handleClick}>Submit</button>;
};

// v5
history.push('/home');
history.replace('/home');

// v6
navigate('/home');
navigate('/home', {replace: true});

1
2
3
4
5
6
7
8
9
10
11
12
13
1. 	import { useNavigate } from 'react-router-dom'

2. let navigate = useNavigate();
function showReplace(id, title) {
// params参数
// navigate(`/home/messages/detail/${id}/${title}`,{replace: true});
// search参数
navigate(`/home/messages/detail/?id=${id}&title=${title}`,{replace: true})
// navigate state参数 【似乎没有这种方式】
// navigate(`/home/messages/detail`, { 'id':id, 'title':title })
};

3. <button onClick={() => showReplace(item.id, item.title)}>replace查看</button>

类组件 v5

1
2
this.props.history.replace(‘router地址’)
this.props.history.push(‘router地址’)

withRouter 【v5有,v6应该是删除了,不能使用】

withRouter:加工一般组件,让一般组件具备路由组件的API,返回值是一个新组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'

class Header extends Component {
back = () => {
// this.props.history.goBack()
}
// forward = () => {
// this.props.history.goForward()
// }
// go = () => {
// this.props.history.go(-2)
// }
render() {
return (
<div>
<h2>React Router Demo111</h2>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.go}>go</button>
</div>
)
}
}
export default withRouter(Header)

BrowserRouter 和 HashRouter 的区别

1.底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
2.path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3.刷新后对路由state参数的影响【v6没有影响】
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!
4.备注:HashRouter可以用于解决一些路径错误相关的问题。

-------------本文结束感谢您的阅读-------------

我的传送门:个人网站GithubGitee