一、概述
1、SPA 的理解
(1)单页 Web 应用(single page web application,SPA)。
(2)整个应用只有一个完整的页面。
(3)点击页面中的链接不会刷新页面,只会做页面的局部更新。
(4)数据都需要通过 ajax 请求获取,并在前端异步展现。
2、路由的理解
2.1 什么是路由?
(1)一个路由就是一个映射关系 ( key:value )。
(2)key 为路径,value可能是 function 或 component。
2.2 路由分类
2.2.1 后端路由
(1)理解:value 是 function,用来处理客户端提交的请求。
(2)注册路由:router.get(path, function(req, res))。
(3)工作过程:当 node 接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据。
2.2.2 前端路由
(1)浏览器端路由,value 是 component,用于展示页面内容。
(2)注册路由:<Route path="/test" component={Test}>。
(3)工作过程:当浏览器的path变为 /test 时,当前路由组件就会变为 Test 组件。
3、react-router-dom 的理解
官方文档:React Router: Declarative Routing for React.js
(1)react 的一个插件库。
(2)专门用来实现一个 SPA 应用。
(3)基于 react 的项目基本都会用到此库。
二、路由的基本使用(语法)
1、准备路由
1.1 下载 react-router-dom 模块
这里下载的是 v5 版本的,v6版本之后学习的时候再下载。
npm i react-router-dom@5
1.2 导入模块
import {Link, Route} from 'react-router-dom'
2、使用路由
在使用路由之前要分析好页面的整体结构,清晰的划分【导航区】和【内容展示区】。
2.1 编写路由链接(导航区)
/* 原生html中,靠 <a> 标签跳转不同的页面 */
/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */
/* 在React中靠路由链接实现切换组件--编写路由链接 */
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
2.2 注册路由(内容展示区 - 指定对应组件)
/* 注册路由 */
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
2.3 使用路由器管理路由
实际上,需要使用路由器对所有的路由进行管理,所以我们在 App 组件外层包裹一个 Router 。
Router 分为: BrowserRouter、HashRouter。
import { BrowserRouter } from "react-router-dom";
const root = createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
)
3、路由组件与一般组件
一般组件 | 路由组件 | |
写法不同 | <Demo /> | <Route path="/demo" component={Demo}/> |
存放位置不同 | compontents | pages |
接收到的 props 不同 | 组件标签传递什么参数,就接收什么参数 | 接收到三个固定的属性 history ( 包含 location ) location match |
三、react-router-dom 相关 API
使用 API 时,记得一定要先导入模块哈。
1、<BrowserRouter> / <HashRouter> / <Route> / <Link>
基础用法中有使用到,不详细说,之后需要再补充。
1.1 BrowserRouter 与 HashRouter 的区别
(1)底层原理不一样:
① BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
② HashRouter使用的是URL的哈希值。
(2)path表现形式不一样:
① BrowserRouter的路径中没有#,例如:localhost:3000/demo/test。
② HashRouter的路径包含#,例如:localhost:3000/#/demo/test。
(3)刷新后对路由state参数的影响:
①BrowserRouter没有任何影响,因为state保存在history对象中。
②HashRouter刷新后会导致路由state参数的丢失!!!
备注:HashRouter可以用于解决一些路径错误相关的问题(比如样式丢失问题)。
2、<NavLink>
2.1 基本使用
官方文档:https://react-router.docschina.org/web/api/NavLink
说明:一个特殊版本的 Link,当它与当前 URL 匹配时,为其渲染元素添加样式属性。
注意:在以下简单案例中,当点击时,会在对应标签增加 active 属性,实现点击部分的高亮效果。(这刚好和 bootstrap.css 设置相似,其他的不一定也是相同的效果)
2.2 <NavLink> 的封装
(1)定义 MyNavLink 组件。
import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
// console.log(this.props);
return (
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
)
}
}
(2)导入使用 MyNavLink 组件实现封装,优点是可以将相同的样式属性封装起来,在编写路由链接时只写不同的部分。
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
3、<Switch>
3.1 概述
官方文档:https://react-router.docschina.org/web/api/Switch
说明:渲染与该地址匹配的第一个子节点 <Route>
或者 <Redirect>
。(默认情况下,是遍历匹配所有路由节点,因此可以这么理解,加上 Switch 后当找到匹配的路由节点时,就不会再往下匹配了)。
3.2 基本使用
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
// 此时不会再显示 Test 组件的内容
<Route path="/home" component={Test}/>
</Switch>
4、<Redirect>
4.1 概述
官方文档:https://react-router.docschina.org/web/api/Redirect
说明:渲染 <Redirect>
将使导航到一个新的地址。这个新的地址会覆盖 history 栈中的当前地址,类似服务器端(HTTP 3xx)的重定向。(应用:当地址没有任何节点可以匹配时,可以指定一个默认出现的页面地址。)
4.2 基本使用
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
5、withRouter 函数
官方文档:https://react-router.docschina.org/web/api/withRouter
含义:您可以通过 withRouter 高阶组件访问 history 对象的属性和最近的 <Route> 的 match。 当路由渲染时,withRouter 会将已经更新的 match、location 和 history 属性传递给被包裹的组件。
使用:
import { withRouter } from 'react-router'
//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)
6、解决样式丢失问题
(1)public/index.html 中引入样式时不写 ./ 写 / (常用)。
<link rel="stylesheet" href="/css/bootstrap.css">
(2)public/index.html 中引入样式时不写 ./ 写 %PUBLIC_URL%(常用)。
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
(3)使用 HashRouter。
7、路由的模糊匹配和严格匹配
(1)默认使用的是模糊匹配。【输入的路径必须包含匹配的路径,且顺序要一致】
(2)开启严格匹配:exact = { true }
<Route exact={true} path="/about" component={About}/>
(3)严格匹配不要随便开启,需要的时候再开启,否则可能会导致无法继续匹配二级路由。
8、嵌套路由的使用
(1)注册子路由时要写上父路由的 path 值。
(2)路由的匹配是按照注册路由的顺序进行的。
<div>
<ul className="nav nav-tabs">
<li>
<MyNavLink to="/home/news">News</MyNavLink>
</li>
<li>
<MyNavLink to="/home/message">Message</MyNavLink>
</li>
</ul>
{/* 注册路由 */}
<Switch>
<Route path="/home/news" component={News}/>
<Route path="/home/message" component={Message}/>
<Redirect to="/home/news"/>
</Switch>
</div>
四、向路由组件传递参数数据
1、params 参数
路由链接(携带参数):
<Link to={'/demo/test/tom/18'}>详情</Link>
注册路由(声明接收):
<Route path="/demo/test/:name/:age" component={Test}/>
接收参数:this.props.match.params
2、search 参数
路由链接(携带参数):
<Link to={'/demo/test?name=tom&age=18'}>详情</Link>
注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/>
接收参数:this.props.location.search
备注:获取到的 search 是 urlencoded 编码字符串,需要借助 query-string 解析。
// 下载插件:npm i query-string // 导入模块 import qs from 'query-string' // 使用 // slice(1)是从[1]开始截取字符串,目的是去除最前面的?号 const result = qs.parse(search.slice(1))
3、state 参数
路由链接(携带参数):
<Link to={{pathname='/demo/test', state:{name:'tom',age:18}}}>详情</Link>
注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/>
接收参数:this.props.location.state
备注:刷新也可以保留住参数,但是参数不在地址上体现,因此清除缓存后参数会消失。
五、编程式路由导航
1、常用 API
借助 this.prosp.history 对象上的 API 对操作路由跳转、前进、后退:
this.prosp.history.push() | push(path, [state]) - (function 类型) | 在 history 堆栈添加一个新条目 |
this.prosp.history.replace() | replace(path, [state]) - (function 类型) | 在 history 堆栈中的当前条目 |
this.prosp.history.goBack() | goBack() - (function 类型) | 等同于 go(-1) |
this.prosp.history.goForward() | goForward() - (function 类型) | 等同于 go(1) |
this.prosp.history.go() | go(n) - (function 类型) | 将 history 堆栈中的指针调整 n |
2、点击事件的实现
// 定义一个按钮实现 push 操作
<button onClick={() => { this.push(msgObj.id, msgObj.title) }}>push</button>
// 利用 this.props.history.push 这个API,这里传参方式是 state
push = (id, title) => {
this.props.history.push('/home/message/detail', { id, title })
}
3、定时跳转的实现
componentDidMount() {
setTimeout(() => {
this.props.history.push('/home/message')
}, 2000);
}