从零构建轻量级项目管理看板:技术路线、心得与优化思考
从零构建轻量级项目管理看板:技术路线、心得与优化思考
作者:Eddygit
日期:2026-10-15
项目:project-manager
一、为什么选择自己造轮子
市面上的项目管理工具(Jira、Trello、飞书项目)功能强大但体量重,对于个人开发者或小团队来说,往往只需要一个简单的看板来追踪任务状态。本项目目标明确:用最轻的技术栈,做一个够用、好看、能跑的 Kanban 看板。
二、技术路线选型
2.1 后端:Node.js + Express
选择 Express 而非 Koa、Fastify,原因有三:
- 生态成熟 — 中间件丰富,遇到问题能快速找到解决方案
- 学习曲线平缓 — 对于小工具项目,不需要框架层面的额外心智负担
- 部署简单 — 单进程、无外部依赖(除 Node.js 运行时),
node server.js即可启动
后端职责极简:提供 CRUD API + 静态文件托管。数据持久化用 JSON 文件,避免引入 SQLite/MongoDB 等数据库,降低部署门槛。
2.2 前端:原生 HTML/CSS/JavaScript
没有用 React、Vue 或任何前端框架,这是经过深思的技术决策:
| 考量维度 | 原生 JS | React/Vue |
|---|---|---|
| 包体积 | 0 KB(浏览器原生) | 30~150 KB(gzip) |
| 构建工具 | 不需要 | 需要 Vite/Webpack |
| 学习成本 | 零额外成本 | 需掌握框架概念 |
| 适合场景 | 交互简单的页面 | 复杂状态管理 |
本工具的交互逻辑(增删改查 + 拖拽)用原生 DOM API 完全可控,引入框架反而是过度工程。
2.3 数据存储:JSON 文件
function loadData() {
return JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
}
function saveData(data) {
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2), 'utf-8');
}
对于单用户、低并发的场景,JSON 文件读写足够。每次请求同步读写文件,虽然性能不如数据库,但在小工具场景下延迟可忽略(< 1ms)。
三、实战经验
3.1 拖拽实现的关键细节
HTML5 Drag and Drop API 看似简单,但有几个坑:
坑 1:dragover 必须调用 e.preventDefault()
不阻止默认行为,浏览器会拒绝 drop 事件,拖拽完全无效:
list.addEventListener('dragover', e => {
e.preventDefault(); // 必须!否则 drop 不触发
list.classList.add('drag-over');
});
坑 2:数据传递用 dataTransfer
拖拽开始时存入 task ID,drop 时取出。不能用闭包变量,因为 drag 和 drop 事件的目标元素不同:
card.addEventListener('dragstart', e => {
e.dataTransfer.setData('text/plain', e.target.dataset.taskId);
});
3.2 XSS 防护
用户输入直接插入 innerHTML 有 XSS 风险。所有用户内容都经过 escapeHtml 处理:
function escapeHtml(s) {
return s.replace(/[&<>"']/g, c => ({
'&': '&', '<': '<', '>': '>',
'"': '"', "'": '''
}[c]));
}
3.3 响应式布局
用 CSS Grid 实现三栏看板,移动端自动切换为单栏:
.board {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 桌面:三栏 */
}
@media (max-width: 768px) {
.board { grid-template-columns: 1fr; } /* 移动:单栏 */
}
3.4 端口检测的必要性
在沙箱/容器环境中,硬编码端口(如 3000)可能已被占用。动态检测空闲端口是更健壮的做法:
for port in range(3000, 9001):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(0.2)
if s.connect_ex(('127.0.0.1', port)) != 0:
print(port); break # 找到空闲端口
四、技术心得
4.1 "够用就好"是真正的工程智慧
最初设计时考虑过加入:用户认证、多人协作、实时同步、任务标签、甘特图视图……但每加一个功能,复杂度就上一个台阶。最终砍到只剩核心:项目 + 任务 + 看板。这种克制让代码量控制在 ~300 行,任何人都能读懂、能改、能部署。
4.2 同步 I/O 在小场景下不是问题
fs.readFileSync / fs.writeFileSync 在高并发下是性能瓶颈,但对于单用户小工具,同步读写反而比异步更易维护(无回调地狱、无竞态条件)。性能优化应该基于实际场景,而非教条。
4.3 无框架的前端依然优雅
用原生 JS 写前端的关键是:把状态和渲染分离。tasks 数组是唯一数据源,renderTasks() 负责根据状态更新 DOM。任何操作(增删改、拖拽)都是:修改状态 → 重新渲染。这其实就是 React 的核心思想,只是没有虚拟 DOM 的开销。
五、存在问题与优化空间
5.1 当前局限
| 问题 | 影响 | 严重程度 |
|---|---|---|
| JSON 文件并发写入无锁 | 多请求同时写入可能丢数据 | 中(单用户下不触发) |
| 无用户认证 | 任何人可访问 API | 高(需部署在内网或加认证) |
| 无数据备份 | 文件损坏则数据全丢 | 中 |
| 前端无离线支持 | 断网后无法操作 | 低 |
| 任务无排序 | 同栏内任务顺序不确定 | 低 |
5.2 优化方向
短期优化(低成本高收益):
- 写入加锁 — 用简单的文件锁或队列串行化写入操作
- 数据备份 — 每次写入时保留上一版本(
data.json.bak),成本极低 - 任务排序 — 给 task 加
order字段,拖拽时更新序号
中期优化(架构演进):
- 迁移到 SQLite — 当数据量增长或需要多用户时,SQLite 是平滑升级路径,无需额外服务
- 加 JWT 认证 — 引入简单的 token 认证,保护 API
- WebSocket 实时同步 — 多人协作场景下,用 ws 推送状态变更
长期思考:
- 是否需要前端框架 — 如果功能持续膨胀(标签、过滤器、甘特图),原生 DOM 操作会变得繁琐,届时迁移到 Vue 3(渐进式,可局部引入)是合理选择
- PWA 离线支持 — 加 Service Worker 缓存静态资源,实现离线可用
六、总结
这个项目的核心收获不是代码本身,而是一个工程理念的验证:在合适的场景下,最简单的技术栈就是最好的技术栈。300 行代码、零构建步骤、一个 node server.js 就能跑起来——这种轻量感是引入一堆框架和构建工具后很难再体会到的。
技术选型不应该跟风,而应该基于实际需求。当你的需求是"做一个够用的看板",那 Express + 原生 JS + JSON 文件就是最优解,不需要更多。
本文为原创技术分享,转载请注明出处。
- 点赞
- 收藏
- 关注作者
评论(0)