LAF项目总结

本文最后更新于:8 个月前

总结失物招领网站项目

一、项目结构

前端项目结构

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
53
54
55
56
57
58
59
│  .env.development	
│ .env.production
│ index.html
│ vite.config.js

└─src
│ App.vue 布局组件,导航栏+内容区+页脚
│ main.js
│ setting.js 全局配置,放置了阿里云OSS客户端的数据(放置敏感信息,所以不能公开)

├─assets 静态文件,svg文件、jpg图片等

├─components
│ CardItem.vue 列表首页的卡片组件
│ CollectionCardItem.vue 个人页“我的收藏”的卡片组件,比前者增加了一些按钮
│ Empty.vue 数据为空时的占位提示组件
│ SearchFilter.vue 列表首页的搜索、筛选组件
│ SubmitCardItem.vue 个人页“发布”的卡片组件,比“收藏”又多了一个按钮,且布局不同

├─css
│ base.css 定义了全局css变量
│ main.css 全局的css配置

├─router
│ index.js 定义全局路由
│ permission.js.js 路由权限管理

├─stores
│ use_post_store.js 存放两种帖子的列表,公共数据
│ use_user_store.js 存放与用户相关的数据

├─utils
│ api_post.js 与帖子信息相关的请求方法
│ api_user.js 与用户相关的请求方法
│ file.js 读取图片文件的工具函数
│ localStorage.js 在localStorage读写“记住密码”状态的工具函数
│ oss.js 创建oss对象
│ request.js 创建axios对象
│ token.js 在localStorage读写token的工具函数

└─views
│ CreateView.vue 发布页
│ DetailView.vue 帖子详情页
│ EditView.vue 帖子编辑页
│ LoserPostView.vue 寻物启事列表页
│ PickerPostView.vue 失物招领列表页
│ SearchView.vue 搜索列表页

├─login
│ ForgetPwdView.vue 忘记密码操作栏(侧边抽屉)
│ index.vue 登陆页
│ RegisterView.vue 注册操作栏(侧边抽屉)

└─person
CollectionsView.vue “我的收藏”容器
index.vue 个人页,集成其余4个组件
PersonInfoEdit.vue 个人信息编辑页
PersonInfoView.vue 个人信息展示栏
SubmitsView.vue “发布”容器

以上的文件都是在开发过程中,陆续新建的,在开发过程中,根据实际需要修改优化过结构。目前项目2.0版本已经完成,现在来看文件的划分,仍存在一些可以优化的地方:

  1. /utils目录下,token.js 和 localStorage.js 其实可以合并为一个文件,因为它们都是涉及到 localStorage 的操作

后端项目结构

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
│  app.js		    CORS、日志打印、数据库连接都在这里配置
│ settings.js 数据库连接配置

├─controllers
│ authentication.js 注册登陆重置密码模块
│ common_data.js 公共数据模块
│ manager.js 管理员模块(暂未使用)
│ post.js 帖子模块
│ user.js 用户模块

├─models
│ common_data.js 定义公共数据模型
│ post.js 定义帖子数据模型
│ user.js 定义用户数据模型

├─routes
│ index.js 用户层面的路由
│ manager.js 管理员层面的路由(暂未使用)

├─utils 工具函数
│ authentication.js 路由权限验证
│ jwt.js 生成和解码jwt
│ logger.js 日志打印器
│ logout.js 登出操作,需要把登出的token写入数据库
│ mailer.js 邮件发送器
│ sts_token.js 阿里云sts_token生成器

二、使用的npm包

前端部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"dependencies": {
"@ant-design/icons-vue": "^6.1.0", // icon库
"ali-oss": "^6.17.1", // 阿里云 oss sdk,用于上传文件
"ant-design-vue": "^3.2.15", // UI库
"axios": "^1.3.4", // XMLHttpRequest库
"pinia": "^2.0.28", // 全局状态管理库
"pinia-plugin-persistedstate": "^3.1.0", // pinia 持久化储存库
"sass": "^1.60.0", // css扩展语言,在vite使用只需要引入sass即可,比以前省事
"vue": "^3.2.45",
"vue-router": "^4.1.6" // 路由管理器
},
}

后端部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"dependencies": {
"@alicloud/pop-core": "^1.7.12", // 生成sts_token需要这个库
"async": "^3.2.4", // 异步操作 库
"cookie-parser": "~1.4.4",
"crypto-js": "^4.1.1", // 加密算法 库
"debug": "~2.6.9",
"express": "~4.16.1",
"file-stream-rotator": "^1.0.0",
"http-errors": "~1.6.3",
"jade": "~1.11.0",
"jsonwebtoken": "^8.5.1", // jwt 库
"log4js": "^6.9.1", // 打印日志的库
"mongoose": "^6.7.4", // 连接mongoDB的库
"morgan": "~1.9.1",
"nodemailer": "^6.9.1" // 发送邮件的库
}
}

三、项目功能

  1. 浏览、发布失物/寻物信息。信息内容包括:
    • 文字:物品信息、用户信息
    • 图片:一组展示物品的图片
    • 时间:物品丢失的大致时间、信息发布的时间
    • 地点:物品丢失的地点、物品现存放的地点
  2. 搜索信息,提供以下搜索方式:
    • 输入关键字
    • 按时间,”当天“、”3天内“、”一周内“、指定起始-终止日期
    • 按校区
    • 按物品分类
    • 以上3种(时间、地点、物品分类)的组合
  3. 举报违规信息
  4. 管理个人的身份信息,管理的信息包括:昵称、学院、邮箱、联系电话
  5. 退出登陆与切换账号
  6. 修改密码
  7. 管理个人的信息记录,包括:
    • 用户自己发布的失物、寻物信息,操作包括:
      • 查看
      • 标记“已找回”
      • 修改编辑
      • 删除
    • 收藏标记的帖子,操作包括:
      • 查看
      • 取消收藏

四、项目亮点

响应设计,支持跨端浏览

五、项目难点

  • 系统设计,很难一次性就设计合理,需要不断迭代优化

    • 数据集合和数据字段的设计很难一次就合理,经历了不断迭代完善

      在做系统设计的时候,对数据字段的设计没有深入的考虑到实际情况,导致一些字段的设计不合理。最典型的一个例子:一个帖子分类字段,值修改了三次,连带数据库修改了三次。

      因为项目叫做 ”LostAndFound“(失物寻回的意思),所以想当然的将帖子的分类也分成了 lostfound,但是直到后面做前端的时候才发现,用这两个名词去区分 失物招领 和 寻物启事 完全不合理,因为语义不明显。

      于是,将帖子的分类改成了 “失主寻失物”和“失物寻失主”;对数据库的数据一顿修改。又过了一段时间,又感觉用这两段中文去描述两种分类还是不理想,描述还是不够明显。于是,又将数据库里面的所有分类字段修改了一次,改成loser_postpicker_post,这次终于达到能够明显区分的状态了。

    • 功能逻辑本身虽然简单,但是边需要考虑的边界情况不少,很难一次性考虑全面

    • 前端逻辑的处理细节也很难一次考虑全面,经常是需要不断地测试、修改

      举几个例子:(1)加载用户信息的时机判断,一开始是进入个人页并发现pinia中没有用户数据时,才加载用户信息。但是这里面隐藏了一个bug,当用户切换账号之后,pinia中却还保留着上一个用户的数据。发现这个bug之后,对用户数据的加载逻辑进行了修改,改成只要一进入个人页就重新加载用户信息,改完之后逻辑才对。

      第一代加载方案,以及存在的问题

      未命名文件 未命名文件 (1)

      为了解决这个bug,推出了 第二代加载方案

      未命名文件 (2)

      实际上,只需要在用户登陆之后执行一次用户数据的加载,再加上pinia持久化储存,就足够了!

      (2)pinia 刚开始并没有采用持久化储存,导致出现了一个bug:如果不是在个人页触发网页刷新,就会使pinia中的用户数据丢失,如果这时再进行发布操作的话,其中绑定记录到用户数据的操作,会因为访问空对象而报错。发现问题后才采取了 pinia 持久化储存的方案 解决了这个bug

    • 越到后面越感觉,需求分析也是一件很难的事情,产生这样感觉的背景是:随着时间的推移,总会感觉有些功能和逻辑上的设计不够合理,不能很好地符合实际需求,所以就会产生推倒一些设计重构的想法。但是实践这些想法,进行重构的工作量完全不小。

  • 涉及到技术比较多,覆盖前后端

    涉及到技术包括:服务器操作、数据库配置、web服务器(Nginx)配置、后端开发、前端开发

    服务器涉及:Linux系统的操作

    数据库涉及:MongoDB数据库的安装、身份验证配置

    web服务器配置涉及:Nginx的安装、https二级域名的配置、反向代理的配置

    后端开发涉及:mongoose库的使用、CORS跨域资源请求配置、JSONWebToken的使用、数据单向加密、路由权限验证、所有前端功能请求逻辑的设计与实现

    前端开发涉及:响应式页面搭建、路由配置、路由权限管理、全局状态管理、等所有功能需求的实现

五、项目技术点

  • 自动化部署
  • 响应式设计
    • 媒体查询
    • grid布局多列自动填充
    • 使用ant-design栅格系统
  • 路由权限控制
  • 登陆跳转
  • 修改个人信息时动态检测内容是否发生更改并反馈
  • 密码加密保存
  • 跨域配置
  • 日志打印

六、体会收获

  1. 不要在主题样式上花过多时间。

    在编码开始之前,特意花了时间去做了一下主题样式的设计,初衷是希望做出点自己的特色。但,毕竟不是专业的,事倍功半,一开始定义的很多全局css变量,颜色、大小等等,最后其实并没有用上。可能是没有经验的缘故,所以规划做的并不是很实用,很多事先的规划事后都因为发现不够实用而推倒了。

    实际上,ant-design已经提供了成熟的主题方案,后面几乎都是套用了ant-design现成的方案,事实证明,这样节省了很多时间。

  2. 避免重复造轮子,要有效地利用现成的东西。

    编码初始,写登陆页时,没有使用ant-design,样式和逻辑都是自己写的,代码量还是很大的,更关键的是,很多细节的处理自己做的并不成熟。所以后来几乎所有组件都使用了ant-design,包括最后登陆页,也用ant-design重构了。

    一开始全部都自己写的初衷是:尽可能地锻炼自己的开发能力。但是发现,光练不行,得先学,学习优秀的案例、ant-design就是这样一个优秀的案例,项目结束后,可以去研究ant-design组件的源码,学习它的思路逻辑。

  3. 对于大多数技术都是:边学边用、边用边学

    写样式的时候,发现写了很多重复的选择器,原因是css不支持嵌套。后面就犹豫要不要用scss,但是又没有用过scss。最后,还是决定花一点时间学习一个scss嵌套的使用,需要用什么就先学什么。

    如果按照过去的思路,可能会先把scss的所有用法都先学一遍,然后再去用它。

    现在发现,其实并不用那样,甚至那样是不合理的。如果都是按照那样的学习思路的话,掌握新技能的速度就会大打折扣,因为要花上很多时间学一时用不到的东西,然后下次要用的时候又忘了,这就是它不合理之处。

  4. 全栈开发,最好的开发步骤是:分模块前后端一起完成

    而不是先做完后端再做前端,也不是先写完前端再写后端。该项目前后端均由我一个人实现,我的实现顺序是先写完后端再写前端,但是在前端开发过程中,发现了许多原先设计并不合理的地方,比如一些数据字段设计不合理,比如方法设计不合理等等。这时候,不仅要写前端,还需要把已经写好的后端进行重写,于是之前的工作就失去价值了,时间也浪费了。

    如果先写前端的话,那么就没有数据来源,也无法实现交互。当然你会说可以用mock这种去模拟数据。但是我既然都要实现后端的,为什么还要多一个步骤,去写一个mock呢?那个是前后端分离开发才用的东西。

  5. 大量的时间其实花在了迭代优化上。

    在开发之前,对数据库和数据集合进行了设计,(1)很多内容其实是没有必要的,但是一开始没有想到,比如用户的个人信息,一开始设置了很多字段:姓名、学号、学院、年级、头像、电话、邮箱等等,但是在前端写到个人页面时,发现其实姓名、年级对于这个网站其实是可有可无的数据,即使存在也发挥不了什么作用,头像其实也是可有可无的东西。所以在开发过程中,又对数据字段进行了优化重构。(2)一些数据字段、数据集合的命名很不合理,比如一开始将帖子的类型采用了lostfound这两个名词,但是后面发现,这两个名词很难进行区分,即使是自己设计的都很容易混淆,所以开发过程中又抽出时间把集合名修改了,导致数据又要重新生成。(3)数据集合的划分一开始不合理,甚至自相矛盾了。用户信息集合里有一个字段,是一个数组,保存用户发布过的帖子记录的外键,但是帖子却根据类型分成了两个,这就导致获取用户信息时无法直接通过外键进行表连接,这就属于是设计不合理带来逻辑处理上的麻烦

    要解决这个问题,一方面要靠经验的积累。另一方面,就如上一点所说的,分模块进行开发,这样能够使修改的范围尽可能小,减小工作量。

  6. 严格统一的规范能够带来开发效率低显著提升

    规范包括很多方面:各种命名规范、请求头字段的规范、响应内容的规范。

    比如响应内容的规范,所有的规范都严格遵守一个格式,前端处理起请求结果时,就能够从容不迫,因为所有的返回内容的结构都是一样的,处理起来的步骤也是相同的,这样省事很多。如果返回内容的格式不一致,有些错误是通过 {success:false}的方式来返回,有些错误是通过错误状态码(如:status:500)来返回的话,前端处理的时候就需要一个一个特别仔细地去查看,这是很费精力和时间的。

    比如css类名命名的规范,如果有些css类名用短横线home-view-container,而有些css类名用下划线home_view_wrapper,另外一些类名又用其它方式的话,引用类名的时候不仅需要特别留心地去查看每一个类名,还特别容易混淆和出错,甚至出错了都很难发现,因为它们之间看起来差别不大。另外,css类名一定不能太简单,要有区分度,否则可能会意外地命中其它标签

  7. 要培养组件化开发的思维

    组件化开发,是指将重复的代码和逻辑抽离成一个单独的文件,称之为组件。这样至少有3个好处:(1)减少冗余的代码,重复的代码都提取到一个文件中了,要用它的地方只需要引入这个文件就可以了,不用重复写一遍基本相同的代码。(2)方便维护,对该部分修改的时候,只需要修改组件文件就可以了;如果不是组件化的话,需要在每个用到它的地方去修改一遍,不仅工作量大而且容易遗漏。(3)将一个复杂的模块从原文件拆分出去,还可以减少原文件的代码行数,能够减小原文件的阅读和维护难度。

    尽管有些时候因为组件件通信的需要,抽离组件不是那么容易,但是其实认真一点就会发现其实也没有那么难,可能只是你不熟而已。

  8. 任务规划可以有效提升开发速度

    我个人在开发时会比较纠结,常常会在细节上花费大量的时间去思考如何优化,从而拖慢了项目的进度;而且有时候做着做着自己就会迷失方向,不知道下一步该做什么了。所以后来自己开始做项目规划,每天开始编码前,将所要完成的任务内容尽可能细化地写下来,遇到需要优化的细节时,写在任务规划中,而不是立即执行,然后对照着任务规划,完成一项记录一下再接着进行下一项。


LAF项目总结
http://timegogo.top/2023/04/18/项目实践/LAF项目总结/
作者
丘智聪
发布于
2023年4月18日
更新于
2023年7月16日
许可协议