SDUDOC实训记录 山东大学创新实训记录
概述
本次实训我主要负责项目前端整体工程和架构的开发建设。通过分析学长老项目SDUDOC编辑器的代码和架构,发现其功能与拓展性远无法满足本次项目实训的需求目标,我只能在其engine实现的基础上,对其模块进行解耦重构,只保留其渲染绘图的能力,并作为编辑器editor部分通过微前端的方式整合进新架构中。
同时,重构后的新前端架构,更符合目前标准的前端工程项目,有利于后期的页面迭代开发和功能拓展。
因此,本人的工作主要在以下几个方面:前端项目整体架构的搭建重构与开发、前端页面的编写、SDUDOC engine的解耦重构和适配引入、前端研发流程的完善规范和创新。
个人工作
1.前端项目架构搭建重构和整体开发
结合个人掌握程度以及项目规模综合判断,前端框架使用React开发,脚手架选用Umi.js,编程语言选用Typescript,状态管理使用Mobx,接口管理使用Rap2,UI组件库使用Antd,多语言国际化解决方案使用React-intl。
项目结构及模块设计:
.
├── package.json
├── .umirc.ts umi配置文件, 在此配置路由
├── mock
├── api.ts umi前端mock数据
├── aseets 存放图片等静态资源
└── src
├── locales 国际化语言文案
├── mobxStore 状态管理, 使用mobx
├── services 网络请求, 带接口类型定义, 使用rap2进行接口同步
├── components 公共组件
├── hooks 自定义hooks
├── pages 所有页面
├── login
├── login.tsx 登录页
├── login.module.less 登录页的局部样式
├── index.less 全局样式
└── index.tsx 首页
└── app.tsx umi运行时配置,启动应用时加载一次
2.开发前端页面
2.1 首页 index
实现优美简介的动画展示产品首页
相关代码实现:
2.2 登录/注册 /login
其中,密码均使用SHA256算法进行加密传输,而非直接以明文密码传给后端,确保数据安全。
const handleLogin = async (formData: IFormData) => {
setLoading(true);
try {
const res = await fetch['POST/auth/login']({
username: formData.userID,
// 对密码进行SHA256加密
password: crypto
.HmacSHA256(formData.password, 'sdudoc')
.toString(crypto.enc.Base64),
});
if (res.code === 200) {
const { token, refreshToken } = res.data;
mobxStore.user.setToken(token);
mobxStore.user.setRefreshToken(refreshToken);
mobxStore.user.setUserId(formData.userID);
if (formData.remember) {
mobxStore.user.setPassword(formData.password);
}
} else {
message.warn(f('login.loginFailure'));
console.error(res);
}
} catch (error) {
message.error(f('login.loginFailure'));
console.error(error);
} finally {
setLoading(false);
}
};
const handleRegister = async (data: IFormData) => {
try {
setLoading(true);
console.log(data);
const res = await fetch['POST/administrator/user/register']({
username: data.userID,
// 对密码进行SHA256加密
password: crypto
.HmacSHA256(data.password, 'sdudoc')
.toString(crypto.enc.Base64),
});
if (res.code === 200) {
message.success(f('login.registerSuc'));
setRegisterForm(false);
registerForm.resetFields();
} else {
message.error(`${f('login.registerFailure')}! ${res.message}`);
}
} catch (error) {
console.error(error);
message.error(f('login.registerFailure'));
} finally {
setLoading(false);
}
};
2.3 古籍管理 /docs
支持古籍增删改查,并可以通过上传解析PDF实现整本古籍的批量录入。
2.4 古籍页面管理 /docPages
同样支持对页面的新增、删除、预览和编辑操作
2.6 古籍编辑器 /editor
编辑器页面支持为古籍页面打标、注释和古文字输入等操作,前端通过将SDUDOC engine进行解耦重构,抽离其核心渲染能力接入新架构中,大大增强其拓展性和可用性。
3.对SDUDOC engine进行解耦重构
抽离绘图引擎
采用微前端框架方式加载,作为编辑器editor页面通过微前端的方式整合进新架构中。
import mobxStore from '@/mobxStore';
import { observer } from 'mobx-react';
import { FC } from 'react';
import { getLocale, useLocation } from 'umi';
import styles from './index.module.less';
const Editor: FC = () => {
const { search } = useLocation();
const params = new URLSearchParams(search);
const pageId = params.get('pageId');
const articleId = params.get('articleId');
return (
<iframe
className={styles['editor-frame']}
src={`http://101.43.27.52:8080/#/index?${new URLSearchParams({
token: mobxStore.user.token,
article: articleId || '',
page: pageId || '',
locale: getLocale(),
}).toString()}`}
/>
);
};
export default observer(Editor);
重构国际化多语言模块
对原engine解耦重构,抽离出国际化多语言模块并集成在前端框架内,实现动态强拓展的多语言国际化文案支持。该部分详细内容可参考本人专篇博客「SDUDOC」前端多语言国际化实现。
重构前端路由模块
实现前端路由鉴权,控制未登录用户无法访问需要登录权限的页面。
import { FC, useEffect } from 'react';
import { observer } from 'mobx-react';
import mobxStore from '@/mobxStore';
import { useHistory, useLocation } from 'umi';
import { message } from 'antd';
import { useFmtMsg } from '@/hooks/useFmtMsg';
const AccessAuth: FC<{}> = (props) => {
const location = useLocation();
const history = useHistory();
const f = useFmtMsg();
// 控制登陆才可访问
useEffect(() => {
if (!mobxStore.user.token) {
message.warn(f('accsessAuth.plsLogin'));
history.push('/login');
}
}, [location.pathname, mobxStore.user.token]);
return <>{props.children}</>;
};
export default observer(AccessAuth);
重构状态管理模块
利用Mobx,将需要全局共享的状态和数据(例如登录状态、用户),统一交给一个容器(MobxStore)管理,并且当容器中任意一个状态更新时,所有引用或依赖该状态的组件/视图都更新。同时,性能也足够优秀,更新的粒度足够细(只有引用了该状态的部分更新,其他部分不更新)、更新的时机足够及时(状态改变立即更新)、状态的更新足够简单。
重构网络请求模块
将网络请求抽离成单独模块的目的在于登录态的管理和错误的统一处理。
由于后端使用jwt的鉴权方式,需要在HTTP请求头中加入Authorization字段,并有着自己的有效期和超时刷新逻辑。
此外,后端接口繁多,入参和返回数据都需要按照文档指定格式请求。有必要根据接口文档维护一份带类型定义的前端请求list,与后端保持同步。
结合项目实际情况,我使用Rapper进行swagger接口同步,并抽离为services前端请求模块。使用rap2进行维护,它是 TypeScript 的最佳拍档,可以帮我生成具有类型定义的请求方案。
这样设计和编码,有以下好处:
- 无需自行书写请求代码,把 HTTP 接口当做函数调用
- 请求参数/返回数据类型化,静态校验、自动补全快到飞起
- 对 React/Redux 特别优化,提供全局数据方案,hooks 轻松使用
4.完善、规范、创新前端研发流程(CICD)
dev分支上通过Test Job后才可发起Merge Request到master主干上。禁止master分支push。搭建Gitlab CI/CD工作流,pipeline流水线任务实现云端构建、集成测试、部署配置。实现持续集成持续部署前端项目至线上服务器http://static.herui.club/sdudoc
总结与感想
当初刚接手SDUDOC这项实训题目时,对这个项目还是知之甚少,并且对我们将要开发的工作内容和目标也不明确。这对开发来说是一个很大的挑战,因为如果连需求定义都不明确不清晰的话,开发可能会难以进行。因此,在前期,我们重点探讨了我们这次实训到底要做一个什么样的产品出来,实现哪些功能,预计要达到一个什么样的效果。回答了上述问题之后,才好做技术选型、方案设计并投入开发。虽然开发过程中由于疫情的影响造成开发进度大量滞后,项目需求和开发工作没有能及时跟进,我们陷入了比较焦虑的状态,因此开发进度和成果相对来说可能不太理想。但我们还是排除万难,本着能做一点是一点、能做更好就去做的态度和迎难而上永不放弃的精神,最终还是做出了一个差强人意但基本功能均已满足使用需求的SDUDOC平台。
总的来说,由于整个项目前端后端都已经将基础架构进行重构升级,因此后续开发难度已经大大降低,拓展性和稳定性已经大大增强。如果有机会的话我们还会继续将本项目进一步完善,打造出一个真正有技术价值的精品,弥补之前我们开发的遗憾。