Nextjs 技术选型
背景
内部要做 AI 相关业务,由于 deepseek 的开源,使得相当多的公司都可以进行私有化的 AI 部署,然后需求下来,开发这边就只能研究一下了 一块砖而已
目前采用 RAGFlow 做知识库管理及模型的调优,但是 RGAFlow 本身是不带权限控制的,不适用于公司每个部门的独立知识库,所以需要自己做一套权限控制,需要后台管理,且 AI 对话需要和第三方软件对接内嵌,就意味着要兼顾 PC 和移动端
- 独立的权限控制意味着需要一个独立的数据库进行管理
- 对话等大部分接口调用 RAGFlow 提供的
- RAGFlow 和 AI 对话之间做一个中间层的权限控制,来防止各部门的知识库混用以及信息泄露
- 后台和移动端功能全部处于此项目中
技术选型
不管选什么技术,一定要优先考虑团队而非自己独断独行,当然如果项目只有你一个人负责就无所谓了。
Nextjs
由于 AI 对话方面就是做一个简单的权限控制,不涉及特别复杂的业务,上 Springboot 的话有点大材小用了(java 也是我们写,我哭死),这种情况下,直接选一个全栈框架是最合适的。
因为内部主 React,选 Next 也是理所应当,全量的 TS,团队有使用 TS 的经验,所以问题不大。
浅谈 ts 和 js 的选择
从个人的观点上,是绝对会使用 TS 的,不管是个人项目还是团队项目,原因如下:
- 项目进度并不是非常紧急,因此有时间将 TS 进行规范,而非虚有其表的 AnyScript
- 项目是长期维护迭代的,而非一次性开发,长久的开发 TS 是绝对的优势
- 在业务长期维护的情况下,友好的类型约束和提示能给团队带来的收益是毋庸置疑的
有相当一部分人讨厌 TS,说 TS 复杂,10 分钟业务 1 小时 TS,真的能如此吗?
在本人看来,纯业务上的 TS 除了标注和类型声明,实在想不到有多复杂的 TS 类型,TS 类型体操又不是人人都能用上的,一般做项目基建,才会使用体操来规范工具类,在这种情况下能说出 10 分钟业务 1 小时 TS 的人,是否可以将其定为能力不足?
目前团队还在维护一个从 19 年迭代到现在的 js 项目,甚至可以称其为岁月的史书了。经过各种人的改动,各种风格迥异的代码写法,成堆的代码屎山...
曾经不知道何时封装的组件,无注释无 API 说明,使用的时候还得一点点看源码,这种感觉真的很痛苦
组件库 antd
个人是更倾向于使用 shadcn,因为很轻量,产物体积小,对于兼容移动端的情况下更适合,但是考虑到团队内部都是常年写后台的,无 UI 设计,而且对 tailwindcss
并不熟练,所以选择 antd 也是一个折中的方案,而且在后台管理中,antd 的起步会更迅速。
orm 框架 drizzle
为什么不是 prisma?
其实在 Nextjs中,prisma
是使用最多的 orm 框架,我也尝试了一下 prisma,但是很遗憾,它并不适合我的团队。因为 prisma
更像一个大而全的 orm,内置了非常多的功能,和对应的上手难度和学习成本。
drizzle
是一个非常轻量级的 ORM,我第一次看文档的时候就觉得这个 orm 非常合适,因为 drizzle
只是对 sql 进行了一次很薄的封装,所有的 API 和 原生 sql 基本保持一致,意思就是只要你会 sql,你就能很轻松的上手 drizzle
,学习成本是非常低的
server action 和 route handlers
在 nextjs 的基础上,连 API 都分了好几种写法
server action
并不是严格意义上的 api 请求,因为它不通过 fetch 调用,直接在服务端组件中调用 action 方法
API Routes
应该是 next 往前版本的写法,所有的请求都耦合在一个方法中,并不直观
Route Handlers
才更像一个 restful API,而且更有利于做迁移,甚至说提供给第三方服务(这点 server action
是做不到的,而且 server action
据说在出现问题的情况下难以排查),所以这点上倒是没什么好纠结的
monorepo
为什么使用 monorepo?
其实当时规划的时候,有考虑到服务端的迁移,因为信创是趋势,一旦使用国产数据库,node 生态是不可能存在支持国产数据库的插件的(达梦数据库有对 typeorm
进行 fork 兼容,看了一下改动,其实就是 )。oracle
套壳,还声称高度兼容 oracle
,只能说国内公司是会弯道超车的
在 node 生态不满足的情况下,只能使用 springboot(MybatisPlus
对国产数据库的支持性挺好的),所以尽量将 drizzle 构成的服务端代码单独抽离,这样如果真的需要迁移,也能够在不和客户端代码耦合的情况下,减小迁移成本。
其次就是一个聊天对话组件,将这一部分抽离,只专注于聊天组件的封装和逻辑处理(基于 antd
的封装)
linter biomejs
在前端工具链 rust 化的现在,使用 rust 的 linter 显然能够极大的提升开发体验,这部分只需要做基础配置,团队成员基本无需关心。
其实在本人在自己写项目时,使用 eslint 和 prettier 也经常会吐槽,插件启动和扫描文件速度很慢,而且在做 lint-stage
时,文件越多,需要的时间就越长,这个缺点也会更明显。
总结体会
搭建一个团队项目,需要考虑的问题是挺多的,此次项目搭建以及基础功能全部由我来完成,从技术选型、布局设计到双端兼容布局、对话组件封装及示例和文档说明、数据库相关等,尽量做到让团队成员减小上手成本。
为什么是团队项目而不是 “企业级” 项目呢?因为自己在 Next 上都是半吊子,都是参考开源高 star 项目和文章进行搭建的,而企业级项目需要考虑和做的基建只会更多更复杂,此处吐槽一下网上的文章,动不动都是企业级项目搭建,赚个噱头,纯标题党,毕竟网上又能有多少会教真本事的呢。
后记
- 4-3 更新:为了减小复杂性和团队成员上手成本,将 monorepo 重新改为了单体项目,并严格对模块进行划分,防止不必要的方法混用
- 4-10 更新:对话 UI 组件参考 antd design x 的样式,在项目中进行了简单实现,也从 x 的源码中学到了不少东西。为什么不直接使用 antd design x 呢?
- 感觉 x 更像一个 KPI 项目,而且活跃度并不高,长远来看并不合适,万一 x 被抛弃而项目中深度使用,这种情况下遇到无法解决的问题是非常危险的
- x 的实现并不复杂,而且我们只需要其中部分组件的核心功能,作为组件库需要考虑的很多边界问题,在项目中并不常见
- AI 对话 UI 的设计非常多样,自己实现一套满足基本需求的对话组件,代码透明,可以根据业务随意变动,无论是 UI 还是逻辑