【实战】十一、看板页面及任务组页面开发(六) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十八)

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
    • 七、Hook,路由,与 URL 状态管理
    • 八、用户选择器与项目编辑功能
    • 九、深入React 状态管理与Redux机制
    • 十、用 react-query 获取数据,管理缓存
    • 十一、看板页面及任务组页面开发
      • 1~3
      • 4~6
      • 7&8
      • 9&10
      • 11&12
      • 13.任务组页面 (下)
      • 14.完成 popover
      • 15.开发完成,部署页面


学习内容来源:React + React Hook + TS 最佳实践-慕课网


相对原教程,我在学习开始时(2023.03)采用的是当前最新版本:

版本
react & react-dom^18.2.0
react-router & react-router-dom^6.11.2
antd^4.24.8
@commitlint/cli & @commitlint/config-conventional^17.4.4
eslint-config-prettier^8.6.0
husky^8.0.3
lint-staged^13.1.2
prettier2.8.4
json-server0.17.2
craco-less^2.0.0
@craco/craco^7.1.0
qs^6.11.0
dayjs^1.11.7
react-helmet^6.1.0
@types/react-helmet^6.1.6
react-query^6.1.0
@welldone-software/why-did-you-render^7.0.1
@emotion/react & @emotion/styled^11.10.6

具体配置、操作和内容会有差异,“坑”也会有所不同。。。


一、项目起航:项目初始化与配置

  • 一、项目起航:项目初始化与配置

二、React 与 Hook 应用:实现项目列表

  • 二、React 与 Hook 应用:实现项目列表

三、TS 应用:JS神助攻 - 强类型

  • 三、 TS 应用:JS神助攻 - 强类型

四、JWT、用户认证与异步请求

  • 四、 JWT、用户认证与异步请求(上)

  • 四、 JWT、用户认证与异步请求(下)

五、CSS 其实很简单 - 用 CSS-in-JS 添加样式

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(上)

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(下)

六、用户体验优化 - 加载中和错误状态处理

  • 六、用户体验优化 - 加载中和错误状态处理(上)

  • 六、用户体验优化 - 加载中和错误状态处理(中)

  • 六、用户体验优化 - 加载中和错误状态处理(下)

七、Hook,路由,与 URL 状态管理

  • 七、Hook,路由,与 URL 状态管理(上)

  • 七、Hook,路由,与 URL 状态管理(中)

  • 七、Hook,路由,与 URL 状态管理(下)

八、用户选择器与项目编辑功能

  • 八、用户选择器与项目编辑功能(上)

  • 八、用户选择器与项目编辑功能(下)

九、深入React 状态管理与Redux机制

  • 九、深入React 状态管理与Redux机制(一)

  • 九、深入React 状态管理与Redux机制(二)

  • 九、深入React 状态管理与Redux机制(三)

  • 九、深入React 状态管理与Redux机制(四)

  • 九、深入React 状态管理与Redux机制(五)

十、用 react-query 获取数据,管理缓存

  • 十、用 react-query 获取数据,管理缓存(上)

  • 十、用 react-query 获取数据,管理缓存(下)

十一、看板页面及任务组页面开发

1~3

  • 十一、看板页面及任务组页面开发(一)

4~6

  • 十一、看板页面及任务组页面开发(二)

7&8

  • 十一、看板页面及任务组页面开发(三)

9&10

  • 十一、看板页面及任务组页面开发(四)

11&12

  • 十一、看板页面及任务组页面开发(五)

13.任务组页面 (下)

新建 src\screens\TaskGroup\components\createTaskGroup.js:

import React, { useEffect } from "react";
import { Button, Drawer, Form, Input, Spin } from "antd";
import { DrawerProps } from "antd/es/drawer";
import styled from "@emotion/styled";
import { ErrorBox } from "components/lib";
import { useAddTaskGroup } from "utils/taskGroup";
import { useTaskGroupsQueryKey } from "screens/TaskGroup/utils";
import { useForm } from "antd/es/form/Form";
import { useProjectIdInUrl } from "screens/ViewBoard/utils";export const CreateTaskGroup = (props: Pick<DrawerProps, "visible"> & { onClose: () => void }
) => {const { mutate: addEpic, isLoading, error } = useAddTaskGroup(useTaskGroupsQueryKey());const [form] = useForm();const projectId = useProjectIdInUrl();const onFinish = async (values: any) => {await addEpic({ ...values, projectId });props.onClose();};useEffect(() => {form.resetFields();}, [form, props.visible]);return (<Drawervisible={props.visible}onClose={props.onClose}forceRender={true}destroyOnClose={true}width={"100%"}><Container>{isLoading ? (<Spin size={"large"} />) : (<><h1>创建任务组</h1><ErrorBox error={error} /><Formform={form}layout={"vertical"}style={{ width: "40rem" }}onFinish={onFinish}><Form.Itemlabel={"名称"}name={"name"}rules={[{ required: true, message: "请输入任务组名" }]}><Input placeholder={"请输入任务组名称"} /></Form.Item><Form.Item style={{ textAlign: "right" }}><Buttonloading={isLoading}type={"primary"}htmlType={"submit"}>提交</Button></Form.Item></Form></>)}</Container></Drawer>);
};const Container = styled.div`height: 80vh;display: flex;flex-direction: column;justify-content: center;align-items: center;
`;

编辑 src\screens\TaskGroup\index.tsx(新增创建任务组功能):

...
import { CreateTaskGroup } from "./components/createTaskGroup";export const TaskGroupIndex = () => {...const [epicCreateOpen, setEpicCreateOpen] = useState(false);...return (<ViewContainer><Row between={true}><h1>{currentProject?.name}任务组</h1><Button onClick={() => setEpicCreateOpen(true)} type={"link"}>创建任务组</Button></Row><Liststyle={{ overflow: "scroll" }}dataSource={taskGroups}itemLayout={"vertical"}renderItem={...}/><CreateTaskGrouponClose={() => setEpicCreateOpen(false)}visible={epicCreateOpen}/></ViewContainer>);
};

查看页面, 尝试新增任务组,功能正常!

注意,若是发现新增或删除后页面数据没有变化,那说明新增或删除时使用的 queryKeyuseQuery 中入参不一致,导致无法正常更新缓存数据

14.完成 popover

功能实现的差不多了,接下来查漏补缺
效果图

从图中可以看到,在项目列表页中,取消或新增收藏项目,页面左上角收藏夹不会自动更新,查看代码并结合控制台发现 ProjectPopover 中没有给 useProjects 传入参数作为 queryKey,因此在缓存中 这两个地方(收藏夹和项目列表并不一致)

编辑 src\screens\ProjectList\components\ProjectPopover.tsx(使用 refetch,在 onOpenChange 中调用):

...export const ProjectPopover = () => {...const { data: projects, refetch } = useProjects();...return (<Popover onOpenChange={() => refetch()} placement="bottom" content={content}>项目</Popover>);
};
...

查看页面,这样功能正常啦!

编辑 src\utils\use-users.ts(改为 react-query 的方式):

import { useHttp } from "utils/http";
import { User } from "types/User";
import { useQuery } from "react-query";export const useUsers = (param?: Partial<User>) => {const client = useHttp();return useQuery<User[]>(["users", param], () =>client("users", { data: param }));
};

新建 src\screens\ProjectList\components\UserPopover.tsx(页面布局有一部分与 ProjectPopover 相同,可以拿过来 src\screens\ProjectList\components\ProjectPopover.tsx):

import React from "react";
import { Divider, List, Popover, Typography } from "antd";
import styled from "@emotion/styled";
import { useUsers } from "utils/use-users";export const UserPopover = () => {const { data: users, refetch } = useUsers();const content = (<ContentContainer><Typography.Text type={"secondary"}>组员列表</Typography.Text><List>{users?.map((user) => (<List.Item key={user.id}><List.Item.Meta title={user.name} /></List.Item>))}</List><Divider /></ContentContainer>);return (<PopoveronVisibleChange={() => refetch()}placement={"bottom"}content={content}><span>组员</span></Popover>);
};const ContentContainer = styled.div`min-width: 30rem;
`;

编辑 src\authenticated-app.tsx(使用 UserPopover 组件)

...
import { UserPopover } from "screens/ProjectList/components/UserPopover";export const AuthenticatedApp = () => {...
};const PageHeader = () => {...return (<Header between={true}><HeaderLeft gap={true}>...<ProjectPopover /><UserPopover /></HeaderLeft><HeaderRight>...</HeaderRight></Header>);
};

查看页面,显示正常!

15.开发完成,部署页面

按视频教程是部署到 github pages

github 上新建一个仓库,仓库名必须为 username.github.io,其中 usernamegithub 用户名。

安装依赖

npm i gh-pages -D
npm i deploy

修改 package.json(配置部署脚本)

{"scripts": {..."predeploy": "npm run build","deploy": "gh-pages -d build -r git@github.com:sindu12jun/sindu12jun.github.io.git -b main"},
}

注意要将 git 链接替换成自己的,并指定分支(main 替代之前 master 作为 github 默认主分支)

安装好后执行:npm run deploy(predeploy 也会自动执行),打包后的文件会自动上传到 github 并生成提交记录

打开部署后的页面,成功!

但是还有个问题,github 如何判断路由是前端路由还是后端路由?

目前点到其他子页面,刷新后 显示404

接下来解决这个问题,参考 spa-github-pages: 使用GitHub Pages 托管单页应用程序

大概步骤如下:

  1. 自定义 404 页面,将当前的路由信息记录下来
  2. 携带路由信息跳转到 index
  3. 进入 index 后检查路由信息,进行还原

新建 public\404.html(配置404页面,解决强制刷新页面时,github pages当做后端路由,从而报错的问题)

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Single Page Apps for GitHub Pages</title><script type="text/javascript">// Single Page Apps for GitHub Pages// MIT License// https://github.com/rafgraph/spa-github-pages// This script takes the current url and converts the path and query// string into just a query string, and then redirects the browser// to the new url with only a query string and hash fragment,// e.g. https://www.foo.tld/one/two?a=b&c=d#qwe, becomes// https://www.foo.tld/?/one/two&a=b~and~c=d#qwe// Note: this 404.html file must be at least 512 bytes for it to work// with Internet Explorer (it is currently > 512 bytes)// If you're creating a Project Pages site and NOT using a custom domain,// then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).// This way the code will only replace the route part of the path, and not// the real directory in which the app resides, for example:// https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes// https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe// Otherwise, leave pathSegmentsToKeep as 0.var pathSegmentsToKeep = 0;var l = window.location;l.replace(l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +l.hash);</script>
</head>
<body>
</body>
</html>

编辑 public\index.html(将路由信息完整的带到 index 中还原,从而实现 SPA 的效果,当然,会看到浏览器地址栏中路由的跳转,体验上稍微差了点,不过功能上已经基本无异。)

<!DOCTYPE html>
<html lang="en"><head>...<title>Jira任务管理系统</title><!-- Start Single Page Apps for GitHub Pages --><script type="text/javascript">// Single Page Apps for GitHub Pages// MIT License// https://github.com/rafgraph/spa-github-pages// This script checks to see if a redirect is present in the query string,// converts it back into the correct url and adds it to the// browser's history using window.history.replaceState(...),// which won't cause the browser to attempt to load the new url.// When the single page app is loaded further down in this file,// the correct url will be waiting in the browser's history for// the single page app to route accordingly.(function(l) {if (l.search[1] === '/' ) {var decoded = l.search.slice(1).split('&').map(function(s) {return s.replace(/~and~/g, '&')}).join('?');window.history.replaceState(null, null,l.pathname.slice(0, -1) + decoded + l.hash);}}(window.location))</script><!-- End Single Page Apps for GitHub Pages --></head><body>...</body>
</html>

英文注释译文:该脚本检查查询字符串中是否存在重定向,将其转换回正确的 url,并使用 window.history.replaceState(…) 将其添加到浏览器的历史记录中,这不会导致浏览器尝试加载新url。当单页应用程序在这个文件中被进一步加载时,正确的 url 将在浏览器的历史记录中等待单页应用程序相应地路由。


解决方案原地址:https://github.com/rafgraph/spa-github-pages

  • 为方便访问,博主在gitee上 fork 了一份 https://gitee.com/OliverDaDa_admin/spa-github-pages

这样部署就完成啦!


部分引用笔记还在草稿阶段,敬请期待。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/60744.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IIS搭建本地电脑服务器:通过内网穿透技术实现公网访问的步骤指南

1.前言 在网上各种教程和介绍中&#xff0c;搭建网页都会借助各种软件的帮助&#xff0c;比如网页运行的Apache和Nginx、数据库软件MySQL和MSSQL之类&#xff0c;为方便用户使用&#xff0c;还出现了XAMPP、PHPStudy、宝塔面板等等一系列集成服务&#xff0c;都是为了方便我们…

应用程序管理工具

应用程序管理是 DevOps 的重要组成部分。它可以定义为在所有阶段监控和管理软件应用程序的可用性、运行状况、性能和功能的过程&#xff0c;包括规划、设计、构建、测试、部署、维护和更新。这意味着应用程序从概念到停止都受到监控。 应用程序管理的重要性 管理应用程序可确…

uni-app中使用iconfont彩色图标

uni-app中使用iconfont彩色图标 大家好&#xff0c;今天我们来学习一下uni-app中使用iconfont彩色图标&#xff0c;好好看&#xff0c;好好学&#xff0c;超详细的 第一步 首先&#xff0c;从iconfont官网&#xff08;iconfont-阿里巴巴矢量图标库&#xff09;选择自己需要的图…

数据库导出工具

之前根据数据库升级需求&#xff0c;需要导出旧版本数据&#xff08;sqlserver 6.5&#xff09;&#xff0c;利用c# winfrom写了一个小工具&#xff0c;导出数据。 →→→→→多了不说&#xff0c;少了不唠。进入正题→→→→ 连接数据库&#xff1a;输入数据库信息 连接成功…

K8S:K8S自动化运维容器Docker集群

文章目录 一.k8s概述1.k8s是什么2.为什么要用K8S3.作用及功能4.k8s容器集群管理系统 二.K8S的特性1.弹性伸缩2.自我修复3.服务发现和复制均衡4.自动发布和回滚5.集中化配置管理和秘钥管理6.存储编排7.任务批量处理运行 三.K8S的集群架构四、K8S的核心组件1、Master组件&#xf…

前端文件、图片直传OOS、分片上传、el-upload上传(vue+elementUI)

前言&#xff1a;基于天翼云的面相对象存储(Object-Oriented Storage&#xff0c;OOS),实现小文件的直接上传&#xff0c;大文件的分片上传。 开发文档地址&#xff1a;网址 上传之前的相关操作&#xff1a;注册账户&#xff0c;创建 AccessKeyId 和 AccessSecretKey之后&…

36种水果和蔬菜识别(pytorch框架,深度卷积网络模型,可以实现照片连续识别和视频识别)

1.效果视频&#xff1a;36种水果和蔬菜识别&#xff08;pytorch框架&#xff0c;深度卷积网络模型&#xff0c;可以实现照片连续识别和视频识别&#xff09;_哔哩哔哩_bilibili 2.项目文件夹 第一个文件夹&#xff08;data&#xff09;&#xff1a; 装载的是原始图像 第二个文…

移动端通讯录相关代码

vue3更新版本 附件地址 代码在附件里噜 太多了不好贴

【MYSQL】排序时 如何将0排到最后,并让其他值按正序展示?

背景&#xff1a;展示排名时需要1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;这样展示但是有些没有排名得数据字段默认值时0&#xff0c;这时直接用ASC就会出现问题 实现效果 实现方式&#xff1a;使用MySQL的ORDER BY语句来实现。以下是一个示例的SQL查…

Python编程——深入了解不可变的元组

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;Python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 目录 一、元组是什么 二、元组的定义 1、相同类型组成元组…

中间件环境搭建配置过程解读

中间件环境搭建 目录 中间件环境搭建xampp 搭建环境Tomcat环境配置安装mysql连接mysql 问题解决 xampp 搭建环境 安装xampp服务集成环境工具 官网地址下载项目压缩包&#xff0c;将项目文件夹放在xampp安装目录的htdocs文件夹下初始化xampp&#xff1a;运行目录内的setup_xamp…

一百六十八、Kettle——用海豚调度器定时调度从Kafka到HDFS的任务脚本(持续更新追踪、持续完善)

一、目的 在实际项目中&#xff0c;从Kafka到HDFS的数据是每天自动生成一个文件&#xff0c;按日期区分。而且Kafka在不断生产数据&#xff0c;因此看看kettle是不是需要时刻运行&#xff1f;能不能按照每日自动生成数据文件&#xff1f; 为了测试实际项目中的海豚定时调度从…

STM32G0 定时器PWM DMA输出驱动WS2812配置 LL库

通过DMA方式输出PWM模拟LED数据信号 优点&#xff1a;不消耗CPU资源 缺点&#xff1a;占用内存较大 STM32CUBEMX配置 定时器配置 定时器通道&#xff1a;TIM3 CH2 分频&#xff1a;0 重装值&#xff1a;79&#xff0c;芯片主频64Mhz&#xff0c;因此PWM输出频率&#xff1a…

USRP 简介,对于NI软件无线电你所需要了解的一切

什么是 USRP 通用软件无线电外设( USRP ) 是由 Ettus Research 及其母公司National Instruments设计和销售的一系列软件定义无线电。USRP 产品系列由Matt Ettus领导的团队开发&#xff0c;被研究实验室、大学和业余爱好者广泛使用。 大多数 USRP 通过高速链路连接到主机&…

Lnmp架构

关闭防火墙 安装依赖包 yum -y install pcre-devel zlib-devel gcc gcc-c make 创建运行用户、组 编译安装Nginx 让系统识别nginx的操作命令 添加Nginx系统服务 vim /lib/systemd/system/nginx.service 编译安装mysql 安装Mysql环境依赖包 创建运行用户 编译安装 cd /opt …

无涯教程-机器学习 - Jupyter Notebook函数

Jupyter笔记本基本上为开发基于Python的数据科学应用程序提供了一个交互式计算环境。它们以前称为ipython笔记本。以下是Jupyter笔记本的一些功能,使其成为Python ML生态系统的最佳组件之一- Jupyter笔记本可以逐步排列代码,图像,文本,输出等内容,从而逐步说明分析过程。 它有…

Java课题笔记~ 综合案例

3.综合案例 3.1 功能介绍 以上是我们在综合案例要实现的功能。除了对数据的增删改查功能外&#xff0c;还有一些复杂的功能&#xff0c;如 批量删除、分页查询、条件查询 等功能 批量删除 功能&#xff1a;每条数据前都有复选框&#xff0c;当我选中多条数据并点击 批量删除 按…

设计模式--装饰者模式(Decorator Pattern)

一、什么是装饰者模式&#xff08;Decorator Pattern&#xff09; 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许你在不修改现有对象的情况下&#xff0c;动态地将新功能附加到对象上。这种模式通过创建一个包装类&#xff0c;…

新手小白想要做好跨境电商独立站,需要考虑哪些要素?

对于不少中小卖家而言&#xff0c;利用独立站出海已然成为下一个跨境热潮。但是采用独立站模式做出海生意前&#xff0c;卖家需要考虑哪些要素&#xff1f; 产品选择 对于国内的卖家来说&#xff0c;依托于国内强大的供应链优势&#xff0c;只要能把握住消费者心态&#xff0…

docker安装及使用-Linux

前提 确保docker支持当前系统版本&#xff0c;docker支持centos 7及以上版本&#xff0c;要求Linux内核版本不低于3.10 cat /etc/redhat-release #查看系统版本 查看内核版本三种方式 cat /proc/version uname -a uname -r 一、安装docker 0、卸载docker&#xff08;根…