type
status
date
slug
summary
tags
category
icon
password
前端路由
概念来源于后端 : 一个路径表示匹配一个服务器资源 /a.html -> a对应的文件资源 /b.html -> b对应的文件资源
共同的思想: 一对一的关系
前端的路由: 一个路由path对应一个组件component,当我们浏览器中访问一个path的时候,path对应的组件会在页面中进行渲染.
前端路由是指在不重新加载整个页面的情况下,根据 URL 的变化来控制用户界面展示不同的内容。这种技术使得单页应用(Single Page Application, SPA)在用户体验上更接近多页应用,但在底层上是通过 JavaScript 来处理页面切换,而不是请求服务器来加载新的页面。
主要概念和工作原理
- URL 映射到组件:在前端路由中,每一个 URL 都对应着一个页面或一个组件。当 URL 改变时,前端路由器(如 React Router、Vue Router)会识别出这个变化,并渲染相应的组件,而不是刷新整个页面。
- 单页应用(SPA):前端路由通常用于 SPA 应用中。整个应用是一个页面,通过路由切换不同的视图来模拟多页效果。初次加载时,浏览器加载了基本的 HTML、CSS 和 JavaScript,以后再切换页面时,只更新视图,而不重新加载整个页面资源。
- History API 和 Hash 模式:
- History 模式:基于 HTML5 的 History API,通过
pushState
和replaceState
改变浏览器的地址栏而不刷新页面。适合现代浏览器。 - Hash 模式:基于 URL 中的哈希(
#
)标记,通过监听 URL 中的#
后面的部分实现路由切换。这种方式更兼容旧浏览器。
前端路由的优势
- 提升用户体验:页面切换速度快,不用频繁刷新和加载资源。
- SPA 体验:提供了更平滑的导航和更自然的应用体验,适合创建类似于桌面应用的 Web 应用。
- 降低服务器负担:很多渲染和处理都在客户端完成,减少服务器的响应需求。
开发环境
使用路由我们还是采用CRA创建项目的方式进行基础环境配置。
- 创建项目并安装所有依赖
- 安装最新的ReactRouter包
- 启动项目
基本使用
路由导航跳转
路由系统中的多个路由之间需要进行路由跳转,并且在跳转的同时有可能需要传递参数进行通信。
1. 声明式导航
声明式导航:通过在模板中通过‘<Link /> 组件描述出要跳转到哪里去’,比如后台管理系统的左侧菜单通常使用这种方式进行。
使用
<Link>
组件<Link>
组件用于在 JSX 中定义一个链接。点击时,不会刷新整个页面,而是进行路由切换。示例:在
Login
页面添加跳转按钮在这个例子中,当用户点击
Go to Article
链接时,React Router 会将用户导航到 /article
路径,并渲染 Article
页面。语法说明:通过给组件的to属性指定要跳转到路由path,组件会渲染为浏览器支持的a链接,如果需要传参直接 通过字符串拼接的方式拼接参数即可。
2. 编程式导航
编程式导航:通过'useNavigate’钩子得到导航方法,然后通过调用方法以命令式的形式进行路由跳转,比如想在登录请求完毕之后跳转就可以选择这种方法,更加灵活。
使用
useNavigate
HookuseNavigate
是 React Router 提供的 Hook,用于在代码中进行编程式导航跳转。这非常适合需要在特定事件(如按钮点击)中进行导航的情况。示例:在
Login
页面添加一个按钮,点击后跳转到 Article
页面在这个示例中,
useNavigate
返回的 navigate
函数可以调用并传入路径 /article
,从而实现页面跳转。语法说明:通过调用navigate方法传入地址path实现跳转。
补充说明(条件跳转):
有时我们需要在满足特定条件后跳转页面,比如用户登录成功后跳转到首页。这种导航方式可以用
useNavigate
和条件语句来实现。示例:用户输入用户名后点击按钮跳转
在这个例子中,用户点击
Login
按钮后,如果用户名输入不为空,就会跳转到 Article
页面。3. 总结
- 声明式导航:通过
<Link>
组件进行声明式的导航跳转。适合在页面结构中直接定义跳转链接,点击时会触发导航。
- 编程式导航:通过
useNavigate
Hook,在事件处理或逻辑中进行跳转。适合需要条件判断、用户操作或特定事件触发的导航。
路由导航传参
1. URL 参数(路径参数)
适用场景:URL 参数是直接嵌入到路由路径中的参数,通常用于标识资源的唯一标识符,比如用户 ID、文章 ID 等。
定义路径参数的路由:
在目标组件中获取 URL 参数:
在
Article
组件中,可以使用 useParams
Hook 来获取路径中的 id
参数:使用
<Link>
或 navigate
传递路径参数:2. 查询参数(Query 参数)
适用场景:查询参数适合用于传递非关键性的、可选的附加信息,如排序方式、过滤条件等。查询参数会以
?key=value
的形式添加到 URL 中。在目标组件中获取查询参数:
在
Article
组件中,可以使用 useLocation
和 URLSearchParams
来获取查询参数:使用
<Link>
或 navigate
传递查询参数:3. state
参数
适用场景:
state
参数用于传递复杂的对象或不希望直接暴露在 URL 中的信息。适合在页面导航过程中传递非持久性的临时数据。在目标组件中获取
state
参数:在
Article
组件中,可以通过 useLocation
的 state
属性来获取 state
参数:使用
<Link>
或 navigate
传递 state
参数:总结
传参方式 | 适用场景 | 传递方式(示例) | 获取方式 |
URL 参数 | 关键参数,标识资源的唯一性 | <Link to="/article/123" /> | useParams() |
查询参数 | 可选附加信息,如排序、过滤条件 | <Link to="/article?id=123&sort=asc"/> | useLocation() + URLSearchParams |
state 参数 | 临时性数据,不希望暴露在 URL 中 | <Link to="/article" state={{id:123}} /> | useLocation().state |
嵌套路由
嵌套路由(Nested Routes)是 React Router 中的一种路由配置方式,它允许在一个路由组件内嵌套其他路由,形成一个层次化的路由结构。嵌套路由通常用于实现布局和子页面的嵌套,或者在父级页面中嵌套子页面。
在一级路由中又嵌套了其他路由,这种关系就叫嵌套路由,嵌套至一级路由内的路由又称作二级路由。
实现步骤:
- 使用 children 属性配置路由嵌套关系
children
是用于定义 嵌套的子路由 的配置项。它是父路由配置中的一个属性,里面可以包含多个子路由配置。通过在父路由中配置children
,我们可以实现父子路由的嵌套。children
是一个数组或对象,数组中包含多个子路由对象。- 每个子路由对象配置了该子路由的路径(
path
)和要渲染的组件(element
)
- 使用 '<Outlet />' 组件配置二级路由渲染位置
<Outlet />
是一个占位符组件,用来渲染父路由匹配后所对应的子路由的内容。它的作用是告诉 React Router,在父组件中哪个位置渲染子路由。
嵌套路由的具体使用
1. 配置父路由和子路由
父路由中配置
children
来嵌套子路由,子路由则会在父组件的 <Outlet />
中渲染。2. 父路由组件使用 <Outlet />
在父路由的组件中,使用
<Outlet />
来占位,表示在这里渲染匹配的子路由内容。3. 子路由组件
子路由组件负责渲染与父路由匹配的内容,并被嵌套在父组件的
<Outlet />
中。4. 渲染路由
通过
RouterProvider
来渲染配置好的路由。嵌套路由的优势
- 层次结构清晰:通过嵌套的方式,可以更清晰地表达页面的层次关系。比如,
Dashboard
页面可以作为父级路由,里面嵌套Profile
和Settings
等子页面。
- 布局复用:父组件可以处理页面布局,子组件只负责具体的内容和功能,避免重复编写布局代码。
- 动态渲染:可以根据路由的不同层级和路径动态地渲染不同的子页面。
嵌套路由的实际应用场景
- 管理后台:常见于管理系统,父路由作为大框架,子路由作为具体的功能页面,如:Dashboard → Profile、Dashboard → Settings、Dashboard → Orders 等。
- 用户中心:用户中心可以有多个子页面,如个人资料、账户设置等。
- 内容管理系统(CMS):在一个复杂的内容管理系统中,嵌套路由可以帮助组织不同的页面视图,并且通过
<Outlet />
让页面呈现不同的内容。
特殊路由
默认路由
当访问的是一级路由时,默认的二级路由组件可以得到渲染,只需要在二级路由的位置去掉path,设置index属性值为true。
在 React Router 中,"默认二级路由"通常指的是在嵌套路由中,某个子路由默认被渲染,而无需通过链接进行手动跳转。通常,这种场景发生在我们希望在父组件渲染时,默认显示某个子组件的情况。
实现默认二级路由的方式:
我们可以通过在父路由的
children
中指定一个没有 path
的路由配置来实现默认二级路由。这样,当父路由被匹配时,默认会渲染这个没有 path
的子路由,作为默认的子组件。实现步骤
- 在父路由的
children
配置中,加入一个没有path
的路由。这一项路由配置会作为默认的二级路由。
- 使用
<Outlet />
在父组件中渲染子路由。
- 在子路由中没有
path
的路由将会自动渲染为默认子组件。
示例
1. 路由配置
2. 父组件中使用 <Outlet />
3. 子组件(Profile 和 Settings)
4. 应用入口文件
关键点
index: true
:在嵌套路由配置中,index: true
表示该路由是默认的子路由。这意味着在父路由匹配时,会自动渲染该子路由,不需要任何路径。它会被渲染为<Outlet />
的默认组件。
在上面的例子中,
Profile
组件作为 index: true
的路由,会在 /dashboard
被访问时默认渲染,而不是需要手动点击 Profile
链接。<Outlet />
:这是一个占位符,用来渲染父路由的子路由。在父路由组件中使用<Outlet />
,并在子路由中渲染相应的组件。对于没有path
的路由(即默认路由),它会在父路由第一次渲染时显示。
默认二级路由的场景
默认二级路由非常适合以下场景:
- 仪表盘或管理后台:通常,父路由(如
/dashboard
)进入时,希望默认显示某个子页面(如个人资料页),不需要用户点击导航项来加载。
- 用户中心:当用户访问
/user
路径时,默认显示用户的资料信息,而不需要先跳转到/user/profile
。
总结
index: true
:标识该子路由为默认路由,当父路由匹配时会自动渲染。
<Outlet />
:用于父组件中渲染子路由组件,默认子路由会在<Outlet />
中显示。
404路由配置
场景:当浏览器输入url的路径在整个路由配置中都找不到对应的path,为了用户体验,可以使用404兜底组件进行渲染。
React Router 提供了一个
*
路径(或 path="*"
)来匹配所有未被定义的路径,这样就可以实现 404 页面。当用户访问一个不存在的路径时,会触发这个 404 路由。实现步骤
- 定义一个 404 页面组件,用于展示 "页面未找到" 或其他自定义的提示。
示例代码
1. 创建 404 页面组件
2. 配置路由并添加 404 路由
3. 应用入口文件
关键点解析
path="*"
:这是 React Router 中的 "catch-all" 路由,表示它会匹配任何未定义的路径。当用户访问一个不存在的页面时,这个路由会被触发,通常用于展示 404 页面。
NotFound
页面:在这个组件中,我们可以定义任意内容,常见的做法是展示一个 404 错误页面,提示用户页面没有找到。
- 路由配置顺序:React Router 会按照路由配置的顺序依次匹配路径。在这种情况下,确保
"*"
路由放在最后面,因为 React Router 会从上到下匹配路径,一旦找到了匹配的路由,就不会继续往下匹配。
路由重定向
除了通过 404 页面处理未知路由,还可以在
*
路由中通过 Navigate
组件进行重定向,例如将用户重定向到首页或其他指定页面。示例:使用 Navigate
重定向
总结
path="*"
:用于匹配所有没有定义的路径,是设置 404 路由的标准方式。
NotFound
组件:当路径未匹配时,可以用一个 404 页面组件来展示错误信息。
- 路由顺序:确保
path="*"
放在所有路由的最后,以避免覆盖其他定义的路由。
- 重定向:可以使用
Navigate
组件来在未匹配的路由中实现重定向。
4o
两种路由模式
各个主流框架的路由常用的路由模式有两种,history模式和hash模式,ReactRouter分别由createBrowerRouter和createHashRouter函数负责创建。
在 React Router 中,常见的两种路由模式是 "Hash 路由" 和 "Browser 路由",它们的主要区别在于 URL 的结构和如何处理浏览器历史记录。
1. Browser 路由(HTML5 History API 路由)
Browser 路由(也叫 HTML5 路由)使用了现代浏览器的 History API 来处理 URL 的变化。它允许我们在浏览器地址栏中使用类似于
https://example.com/path/to/page
这样的纯粹路径。特点:
- URL 没有
#
符号:URL 路径就是你在浏览器地址栏中看到的路径。
- 路径与实际文件相对应:这意味着我们在地址栏输入的路径与服务器上的路径应匹配。
- 支持浏览器历史记录:用户可以使用浏览器的前进、后退按钮来进行导航,且不需要重新加载页面。
- 需要服务端支持:因为浏览器路由模式是基于路径的,所以如果直接刷新页面或访问某个路径(比如
https://example.com/about
),需要确保服务器能够正确地处理这个请求并返回应用的index.html
文件。
使用方法:
适用场景:
- 对于支持后端服务的 SPA(单页面应用),通常使用 Browser 路由模式。
- 需要干净、没有
#
符号的 URL。
问题:
- 如果用户直接在浏览器地址栏输入路径(例如
/about
),会向服务器发起请求,这就要求服务器能够处理这个路径请求并返回应用的 HTML 文件。否则会导致 404 错误。
2. Hash 路由(哈希路由)
Hash 路由使用 URL 的哈希部分(即
#
符号后面的部分)来模拟不同的页面。例如:https://example.com/#/about
。这不会改变浏览器的地址栏的主路径部分,而是改变 #
后面的内容。特点:
- URL 包含
#
:路径会带有#
符号,后面跟着路由的路径。
- 不需要服务器支持:哈希路由不会与服务器发生交互,它只会修改浏览器的哈希值,不会发送请求到服务器。因此,无论刷新页面,服务器都不会受到影响。
- 不影响浏览器历史记录:浏览器依然可以记住路由的变化,并支持后退和前进按钮,但哈希部分不会导致浏览器重新加载页面。
使用方法:
适用场景:
- 如果应用部署在静态资源服务器上,没有后端支持或者后端无法配置 URL 重写规则时,使用 Hash 路由是一种比较常见的选择。
- 当应用需要运行在不支持服务器配置的环境下,比如 GitHub Pages,或者不希望为每个路由配置服务器的重定向时,使用 Hash 路由会更加方便。
问题:
- URL 中包含
#
符号,虽然这不影响应用的运行,但可能让用户体验不如 Browser 路由。
对比
特性 | Browser 路由 | Hash 路由 |
URL 样式 | https://example.com/about | https://example.com/#/about |
服务器支持 | 需要配置,服务器必须正确处理路径 | 无需服务器支持,路径不会向服务器请求 |
浏览器刷新 | 刷新时需要服务器支持返回应用的 index.html | 刷新不会影响,直接加载应用 |
历史记录支持 | 支持,使用浏览器的历史 API(前进、后退按钮) | 支持,浏览器的历史记录会基于哈希值变化 |
路径匹配 | 直接匹配实际路径,如 /about | 使用 # 后面的路径,如 #/about |
总结
- Browser 路由:
- URL 清晰干净,没有
#
符号。 - 需要服务器支持,适合传统的有后端支持的单页面应用(SPA)。
- 可以处理基于路径的路由,支持现代浏览器的历史记录 API。
- Hash 路由:
- URL 包含
#
符号。 - 不需要服务器支持,适合没有后端或者无法配置路由的环境。
- 使用简单,不依赖服务器配置,但不如 Browser 路由那样清晰优雅。
两者选择的关键在于应用的部署环境。如果有完整的服务器支持,通常选择 Browser 路由,因为它可以提供更简洁的 URL 结构;如果是在没有后端路由配置的情况下,或者应用部署在静态资源服务器上,则可以选择 Hash 路由。
记账本demo
环境搭建
使用CRA创建项目,并安装必要依赖,包括下列基础包:
- Redux状态管理: @reduxjs/tookit、react-redux
- 路由:react-router-dom
- 时间处理:dayjs
- class类名处理:classnames
- 移动端组件库:antd-mobile
- 请求插件:axios
- scss:是一种后缀名为.scss的预编译CSS语言,支持一些原生CSS不支持的高级用法,比如变量使用,嵌套语法等,使用SCSS可以让样式代码更加高效灵活
安装的方式npm install sass -D
目录:
配置别名路径
- 路径解析配置(webpack),把@/解析为src/ (在不同的构建工具和框架中实现不一样)[解决的是编译解析]
- CRA本身把webpack配置包装到了黑盒里无法直接修改,需要借助一个插件 -craco。
- 安装craco: npm i -D @craco/craco
- 项目根目录下创建配置未见:craco.config.js
- 配置文件中添加路径解析配置
- 包文件中配置启动和打包命令
- 路径联想配置(VsCode),VsCode在输入@/[路径联想]
- VsCode的联想配置,需要我们在项目目录下添加jsconfig.json文件,加入配置之后VsCode会自动读取配置帮助我们自动联想提示。
- 根目录下新增配置文件:jsconfig.json
- 添加路径提示配置
数据mock
在前后端分离的开发模式下,前端可以在没有实际后端接口的支持下先进行接口数据的模拟,进行正常的业务开发功能。
常见的mock服务
json-server实现数据Mock
json-server是一个node包,可以在不到30秒内获得零编码的完整的Mock服务。
- 项目中安装json-server:npm i -D json-server@0.17.3
- 准备一个json文件(和src同级,创建一个server目录,创建一个data.json)
- 添加启动命令
- 访问接口进行测试
补充说明
可以配置npm run start 执行的时候同时把mock数据服务一起启动,这样就可以执行两个命令
项目开发
- 整体路由设计,根据UI和交互分析
- antD-mobile主题定制,具体看官方文档‣
- 定义store
- 异步请求获取的数据,例如billState
- 具体设计分析,看文档→找相似的demo→复制代码跑通→定制化修改
- 使用antD的TabBar标签栏进行布局以及路由的切换。
computeExpensiveValue
是一个计算函数,只会在依赖项[a, b]
发生变化时重新执行。memoizedValue
是记住的计算结果,当依赖项未变时,它会直接返回上次的计算结果,从而避免重复计算。useMemo
接收两个参数:- 第一个参数是一个计算函数,返回需要缓存的值。
- 第二个参数是一个依赖项数组,只有在其中的变量发生变化时,
useMemo
才会重新计算。 - 避免高计算量的重复执行:如果一个计算很耗时,比如大数据量的排序、过滤等,可以用
useMemo
来记住计算结果,减少开销。 - 优化子组件:如果一个子组件只依赖于某些值,那么使用
useMemo
可以确保传递给子组件的属性不会在不必要的渲染时更改。 - 登录-token持久化
- axios请求拦截器注入token
- token 必须传递:
token
始终通过拦截器从getToken
获取并添加到请求头。 - userId 可选:如果需要
userId
,可以通过各接口的参数传递。例如,在getUserInfo
中,userId
作为查询参数传递。 Bearer ${token}
是一种常见的 HTTP 认证方式,通常用于在请求头中传递访问令牌(token)进行身份验证。- Bearer:这是认证方案的类型,它表明该请求使用的是 "Bearer token"(承载令牌)。
Bearer
是一种认证方式的标准术语,意味着后面跟着的是访问令牌。它告诉服务器请求者需要提供令牌来进行身份验证。 ${token}
:这是 JavaScript 的模板字符串语法,用于将变量token
的值插入到字符串中。例如,如果token
的值是abc123xyz
,那么Bearer ${token}
就会变成Bearer abc123xyz
。- 使用token做路由权限控制
5. 计算选择月份的数据统计
useMemo用法
React中,
useMemo
是一个用于性能优化的钩子函数。它允许你记住计算结果,以避免在每次渲染时重新计算某些复杂、开销大的数据。以下是它的基本用法和使用场景。基本用法
useMemo
的语法如下:参数解释
使用场景
需求实现代码
除此之外还可以用sessionStorage会话级别存储或者redux-persist插件.
对于token的处理可以抽到util中进行方法复用.
Token作为用户的一个标识数据,后端很多接口都会以它作为接口权限判断的依据;请求拦截器注入Token之后,所有用到Axios实例的接口请求都自动携带了Token
请求相关的逻辑我们一般都封装在utils/request.js中
说明
具体来说:
- 作者:coderma4k
- 链接:https://coderma4k.com//article/135bd2a3-ceeb-80d5-86bf-d93eeb8f998f
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。