槐序笔记:一个个人笔记系统的构建过程
在信息碎片化的时代,如何高效地收集、整理和内化知识,是许多人都面临的挑战。我们常常在多个平台间切换,笔记散落各处,难以形成体系。为了解决这一痛点,同时也为了深入实践全栈开发技能,我们构建了“槐序笔记” —— 一个干净、专注、功能完整的个人笔记与知识管理平台。
一、 项目概览
“槐序”一词取自“槐花飘香,时序更迭”,寓意着在时间的流转中,通过记录和思考来沉淀智慧。槐序笔记不仅是一个笔记应用,更是一个个人知识库和微型博客平台。它允许用户创建私有笔记,并一键将内容公开分享,构建属于自己的个人主页。
核心功能一览
-
笔记管理 (CRUD):支持笔记的创建、读取、更新和删除,并具备分类、置顶、搜索和排序功能。
-
回收站机制:删除的笔记会先进入回收站,提供误删恢复的缓冲期,支持彻底删除。
-
个人主页与社交分享:每个用户拥有独立的公开主页,可展示用户信息与公开笔记,访客无需登录即可浏览。
-
公开笔记分享:用户可将笔记设为公开,并生成分享链接,方便在社交平台传播。
-
个人资料与主题定制:支持用户自定义头像、昵称、个人简介,并内置了深色/浅色模式及多种主题色。
-
PWA 支持:项目支持渐进式 Web 应用(PWA),可安装到桌面或手机,提供接近原生应用的体验。
-
Markdown 与附件支持:编辑器支持 Markdown 语法,并集成了文件上传功能,让笔记内容更丰富。
二、 技术栈详解
槐序笔记采用前后端分离的架构,选用了现代、成熟的 Web 技术栈,兼顾了开发效率与运行性能。
后端技术栈
-
核心框架: Node.js + Express.js
-
轻量、高效,非常适合构建 API 服务。
-
-
数据库: SQLite + Prisma ORM
-
SQLite 提供了零配置、文件即数据库的便捷性;Prisma 提供了类型安全的数据库访问和便捷的迁移工具,极大地提升了开发体验。
-
-
认证与安全: JWT (JSON Web Tokens) + Bcrypt
-
使用 JWT 进行无状态身份认证,使用 Bcrypt 对用户密码进行加盐哈希加密,保障用户数据安全。
-
-
文件存储: Multer
-
用于处理
multipart/form-data类型的文件上传请求,支持头像和笔记附件上传。
-
前端技术栈
-
核心框架: 原生 JavaScript (ES Modules)
-
项目未使用 React/Vue 等重型框架,而是采用原生 JS 模块化开发,轻量、直接,掌控力强。
-
-
构建工具: Vite
-
Vite 提供了极速的开发服务器启动和热更新,以及高效的打包能力,大幅优化了开发体验。
-
-
样式与主题: CSS3 (自定义属性/变量)
-
完全使用原生 CSS 构建界面,并通过 CSS 变量(
var(--primary))实现了动态主题切换(深色/浅色模式与多种主题色)。
-
-
Markdown 解析: Marked.js
-
一个高性能的 Markdown 解析器,用于将用户输入的 Markdown 格式文本实时渲染为 HTML,展示在笔记预览和详情页中。
-
-
HTTP 客户端: Axios
-
用于在前端发起 HTTP 请求,并利用其拦截器统一处理认证 Token 和全局错误。
-
-
PWA: 通过
manifest.json和sw.js(Service Worker) 实现离线缓存和可安装性。
服务端与部署 (生产环境)
-
Web 服务器: Nginx
-
作为反向代理服务器,负责处理静态资源请求,并将 API 请求代理转发至后端服务,同时处理 HTTPS 和负载均衡。
-
-
进程管理: PM2 或 Node 原生守护进程
-
用于在生产环境中管理 Node.js 进程,提供日志记录、崩溃自动重启和负载均衡功能。
-
-
操作系统与环境: Linux (CentOS/Ubuntu) + 宝塔面板
-
项目运行于 Linux 服务器,通过宝塔面板进行可视化的网站、数据库和文件管理。
-
三、 核心模块深度解析
1. 笔记管理模块
笔记管理是系统的核心。其数据模型(Prisma Schema)设计如下:
model Note {
id Int @id @default(autoincrement())
title String
content String @default("")
category String @default("默认")
isPinned Boolean @default(false)
isPublic Boolean @default(false)
viewCount Int @default(0)
userId Int
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tags Tag[] @relation("NoteTags")
}
该设计支持了关键业务逻辑:
-
软删除:通过
deletedAt字段实现回收站功能。 -
置顶:通过
isPinned字段实现笔记置顶。 -
公开分享:通过
isPublic字段控制笔记的公开状态。 -
用户关联:通过
userId与用户关联,并设置onDelete: Cascade,删除用户时其所有笔记也将被删除。
2. 用户认证与权限控制
认证流程基于 JWT 实现,通过前端请求拦截器和后端的中间件形成闭环。
-
登录/注册:用户在登录页提交凭证,后端验证通过后生成一个包含
userId和role的 JWT Token 返回给前端。 -
Token 存储:前端将 Token 存储在
localStorage中。 -
请求拦截:前端 Axios 请求拦截器会自动在所有请求头中添加
Authorization: Bearer <token>。 -
身份验证中间件:后端中间件(
auth.js)会拦截所有需要登录的路由,验证 Token 的有效性,并将解码后的用户信息挂载到req.user上,供后续控制器使用。
// 核心验证中间件逻辑 const decoded = verifyToken(token); if (!decoded) { return res.status(401).json({ msg: '登录已过期,请重新登录' }); } req.user = decoded; next();
3. 个人主页系统
个人主页是槐序笔记的亮点功能,它整合了用户信息展示与内容发布。
-
路由设计:前端通过
profile.html?user=username的方式加载个人主页,后端通过GET /api/user/:username获取用户公开信息。 -
公开笔记查询:后端控制器在查询笔记时,会严格过滤
isPublic: true和deletedAt: null的条件,确保只有公开且未被删除的笔记才会被展示。 -
主题同步:个人主页和设置页面会读取
localStorage中的用户主题偏好,实现与主站一致的视觉体验。
4. 附件上传功能
为笔记添加附件(图片、文档等)是增强内容表现力的重要途径。
-
后端:使用
Multer中间件处理multipart/form-data,将文件保存到服务器特定目录(/public/uploads/attachments),并将访问 URL 返回给前端。 -
前端编辑器:在编辑器工具栏添加了
📎按钮,点击后触发隐藏的input[type="file"],上传成功后自动在光标处插入 Markdown 格式的图片或链接语法(如或[📎 文件名](url))。
四、 开发与部署中的关键挑战与解决方案
在项目开发和部署过程中,我们遇到并解决了几个关键问题,这些经历也塑造了项目的最终形态。
1. 数据库迁移与字段兼容性问题
挑战:在项目迭代中,我们需要为 User 表新增 updatedAt 字段,并设定为 DateTime 类型。然而,已有的旧数据中该字段为空,导致 Prisma 迁移失败 (P2032 错误)。
解决方案:我们采用了方案一:将 Schema 中的 updatedAt 字段设为可选(DateTime?),并在代码中手动管理该字段的更新,例如:
data: { // ... 其他字段 updatedAt: new Date() }
启示:在修改已有生产数据库结构时,必须谨慎考虑数据的兼容性,通过“字段可选”或“设置默认值”的方式来平滑过渡。
2. 动态路由与静态路径的冲突
挑战:在 user.routes.js 中,我们将 /:username 这样的动态路由放在了 router.use(auth) 中间件之后,导致所有用户相关的请求(如 /profile)都会被 /:username 捕获,并去数据库查询名为 profile 的用户,最终返回 404。
解决方案:我们调整了路由顺序,将所有的静态/固定路径(如 /profile, /me, /theme)以及需要鉴权的路由,全部放在动态路由 /:username 之前。
启示:在 Express 中,路由匹配是按顺序进行的。动态路由(尤其是 /:xxx 形式)必须定义在最后,否则它会“吞噬”所有以该路径开头的请求。
3. 前端构建与资源引用问题
挑战:在 Vite 构建过程中,settings.html 中硬编码的 assets/settings-xxx.js 文件路径找不到,导致构建失败。
解决方案:我们在 settings.html 中删除了所有硬编码的 <script> 和 <link> 标签,改为使用 type="module" 并直接引用源码文件(如 /src/api.js),然后由 Vite 在构建时自动处理依赖和路径。
启示:在 Vite 等现代构建工具中,应充分利用其模块解析和依赖追踪能力,避免手动管理哈希文件名。
五、 未来展望
槐序笔记目前已经具备了作为一个个人知识管理工具的核心能力。未来,我们计划从以下几个方向继续完善它:
-
AI 功能集成:引入 AI 能力,实现笔记的智能摘要、标签自动生成或写作辅助。
-
全文搜索:当前搜索基于 SQL
LIKE,未来可集成 Elasticsearch 或 Meilisearch 等搜索引擎,提供更快速、更准确的全文检索。 -
移动端适配与优化:进一步优化移动端的交互体验,使其在手机上的使用更加流畅和自然。
-
数据导入/导出:支持从其他笔记应用(如 Notion、语雀)导入数据,并提供更丰富的导出格式(如 PDF、HTML)。
-
协作功能:探索笔记的协同编辑或评论功能,从“个人知识库”迈向“轻量级内容协作平台”。
六、 结语
槐序笔记是一个从零开始、完全自主构建的全栈项目。它不仅是一个可用的工具,更是对现代 Web 开发技术栈的一次深入实践。从数据库设计、后端 API 开发,到前端交互、主题系统,再到最终的部署上线,每个环节都充满了挑战与学习的乐趣。
我们希望槐序笔记能为你提供一个清爽、高效的知识管理空间,也希望能为正在学习全栈开发的同好们提供一份有价值的实践参考。
项目将持续迭代,欢迎体验和反馈。