一、简介
求职季里,很多人需要「能改、能预览、能导出」的简历工具。本文基于一个面向用户完全免费的在线简历项目,梳理其技术选型与简历数据从编辑到导出、再到云端保存的整体方案,供同类站点或 side project 参考。
二、产品定位:为什么强调「完全免费」
- 核心能力:在线编辑、多模板、实时预览、导出 PDF、账号体系下云端多份简历、可选公开分享等。
- 免费策略:编辑、保存、导出等主流程对用户不收费、不设付费墙(运营与合规以你实际上线规则为准)。
- 引流价值:技术博客读者更关心「能不能白嫖干活」与「实现是否靠谱」;把「免费 + 技术透明」写清楚,有利于自然传播。
三、整体架构概览
典型形态为前后端分离:
| 层级 | 职责 |
|---|---|
| 前端 | 页面路由、国际化、编辑器 UI、客户端状态、PDF 渲染与下载、调用后端 API |
| 后端 | 用户与会话、简历 CRUD、模板与内容管理、校验、静态资源策略接口等 |
| 数据层 | 关系型数据库存储用户与简历元数据;正文为结构化 JSON |
对外 API 可按公开 / 需登录拆分(例如公开读模板、分享页按短链标识读取;私有写操作带 Token + Cookie 会话),便于网关或反向代理做路径隔离。
四、前端技术栈与选型理由
主要依赖(以仓库 package.json 为准):
- Next.js:App Router、服务端与客户端组件分工,利于 SEO 与首屏;独立部署可用 standalone 打包脚本思路。
- React 19 + TypeScript:类型约束对「简历大 JSON」结构特别重要,减少字段漏传。
- Tailwind CSS 4:快速搭编辑器与营销页布局,保持视觉统一。
- next-intl:多语言文案与路由级 locale,避免首屏硬编码中文占位闪烁等问题。
- Redux Toolkit:经典编辑器场景下,将整份
ResumeData放在单一 slice,便于「整包替换」与预览组件订阅。 - Tiptap(ProseMirror):专业技能、自我评价、荣誉说明等模块用 JSONContent 存富文本,兼顾编辑体验与可序列化。
- @react-pdf/renderer:在浏览器端生成与简历结构一致的 PDF,与右侧/弹层预览同源,减少「网页一套、打印一套」的偏差。
- react-pdf + pdf.js worker:预览链路中与 PDF 相关的展示能力(具体页面按项目配置)。
- Radix UI:无障碍友好的 Select 等基础件。
- Fetch 封装:统一
token请求头、credentials: 'include'以配合服务端 Session,并对 401 做登出跳转。
小结:前端把「强类型简历模型 + 富文本 + Redux 单源 + React-PDF 同源导出」绑在一起,是体验与一致性的关键。
五、后端技术栈与职责
主要依赖(以仓库 package.json 为准):
- Koa 2:轻量中间件模型,适合中小型 BFF。
- Sequelize + MySQL:用户、简历、模板等表结构清晰;简历正文可用 LONGTEXT 存整份 JSON。
- koa-session:与前端 Cookie 配合,形成「Token + Session」的常见组合(实现细节略)。
- Joi:入参校验,避免脏 JSON 写入数据库。
- bcryptjs:密码哈希。
- 其他:邮件/验证码、图形验证码、日志与错误处理中间件等,按业务模块拆分 controller。
小结:后端偏「薄业务 + 强校验 + 清晰路由前缀(public/private)」,复杂排版仍交给前端 PDF 引擎,数据库只保证一致性与可检索字段(如用户 ID、分享 slug、是否公开等)。
六、简历数据模型与「实现方案」
6.1 领域模型:一份简历是什么
前端用 TypeScript 定义统一的 ResumeData,大致包括:
- 个人信息、求职意向
- 教育 / 工作 / 实习 / 项目 / 校园经历 等列表模块
- 专业技能(Tiptap JSON)、荣誉奖项(列表 + 可选富文本)、自我评价(Tiptap JSON)
- 模块显隐、编辑器内模块顺序 等配置项
初始状态使用「空简历」常量,避免非中文环境首屏出现错误占位文案;再根据语言、本地缓存或接口数据 hydrate。
6.2 持久化:数据库里怎么存
简历表可抽象为:
- 归属用户
- 标题、模板 ID
content:整份个人数据的 JSON 字符串- 是否公开、分享用 slug、可选预览图地址(例如保存时由客户端生成预览图再上传对象存储,仅存 URL)
并针对 userId、isPublic + slug 等建立索引,保证列表与分享页查询性能。
6.3 编辑与预览:数据怎么流转
- 用户打开编辑器 → 从接口拉取或生成默认简历 → normalize 富文本字段(兼容旧版本数据结构)。
- 编辑过程中 → Redux 持有当前
ResumeData。 - 右侧/分栏 PDF 预览 → 使用与导出相同的
ResumeDocument组件树,保证「所见即所得」。 - 保存 → 将
ResumeData序列化为 JSON 调用后端更新;可选上传预览图。
6.4 导出 PDF
使用 @react-pdf/renderer 的 pdf() 在客户端生成 Blob,再触发下载或用于生成预览缩略图(例如转成 PNG 上传),减轻服务端 CPU 压力,同时版式逻辑集中在前端代码库,迭代成本低。
6.5 分享
「公开分享」时生成唯一 slug,分享页通过公开只读接口按 slug 查询,无需登录即可访问脱敏后的展示内容(具体字段展示策略由前端模板控制)。