个人博客小工具:技术路线、心得与实战经验分享
个人博客小工具:技术路线、心得与实战经验分享
作者:Eddygit
日期:2026-10-07
标签:Node.js、Express、Markdown、全栈、轻量级
一、项目背景与目标
作为一个经常记录技术笔记的开发者,我一直想要一个足够简单、足够轻量的个人博客工具。市面上的博客系统要么太重(WordPress、Ghost),要么依赖太多(Hexo 需要 Node 生态、Hugo 需要 Go 环境),要么托管在第三方平台(博客园、掘金)无法完全掌控数据。
于是目标很明确:
- 零数据库依赖:数据用 JSON 文件存储,拷贝即备份
- 零前端框架:纯 HTML/CSS/JS,不引入 React/Vue
- Markdown 原生支持:写作者最熟悉的标记语言
- 一个命令启动:
npm start就能跑
二、技术路线选型
2.1 后端:Node.js + Express
选择 Express 而非 Fastify、Koa,原因很简单:生态最成熟,中间件最丰富,遇到问题搜索结果最多。对于一个轻量工具,框架的"快"远不如"好查"重要。
核心设计是 6 个 RESTful API:
GET /api/posts # 列表(支持 ?tag= 和 ?q= 筛选)
GET /api/posts/:id # 详情
POST /api/posts # 创建
PUT /api/posts/:id # 更新
DELETE /api/posts/:id # 删除
GET /api/tags # 所有标签
2.2 数据存储:JSON 文件
没有用 SQLite、LowDB,而是直接 fs.readFileSync / fs.writeFileSync 读写 JSON 文件。
为什么?
- 博客文章数量通常在百篇以内,JSON 完全够用
- 文件即数据库,
cp posts.json posts.json.bak就是备份 - 零依赖、零配置、可读性强
- 后续迁移到数据库只需替换
readPosts()/writePosts()两个函数
代价:不支持并发写入。但个人博客场景下,只有一个人在写,这个问题可以忽略。
2.3 前端:原生三件套
没有用 React、Vue,甚至没引入 jQuery。原因:
- 页面只有 3 个:列表、详情、编辑。状态管理简单到不需要虚拟 DOM
- 打包工具为零:没有 webpack、vite、babel,没有构建步骤
- CDN 引入 marked.js:唯一的外部依赖,用于 Markdown 渲染
前端 JS 按页面拆分:common.js(公共工具)+ index.js / post.js / editor.js(各页面逻辑)。
2.4 Markdown 渲染:marked.js
选择 marked 而非 markdown-it、remark:
- 体积小(~30KB)
- API 极简:
marked.parse(text)就完事 - 支持 GFM(表格、删除线、任务列表)
在编辑页做了实时预览:输入框 input 事件触发 marked.parse(),右侧即时渲染。写作者能边写边看效果,这是体验的关键。
三、实战经验
3.1 文章列表的排序与筛选
最初把排序、筛选逻辑放在前端,后来发现文章多了以后前端过滤有卡顿。改为后端筛选:API 接收 ?tag= 和 ?q= 参数,在服务端完成过滤后返回。前端只负责渲染。
// 后端筛选
if (tag) posts = posts.filter(p => p.tags.includes(tag));
if (q) posts = posts.filter(p =>
p.title.toLowerCase().includes(q.toLowerCase()) ||
p.content.toLowerCase().includes(q.toLowerCase())
);
3.2 搜索防抖
列表页搜索框用了 300ms 防抖:
let searchTimer;
searchInput.addEventListener('input', () => {
clearTimeout(searchTimer);
searchTimer = setTimeout(loadPosts, 300);
});
避免每输入一个字符就请求一次 API。300ms 是经验值——比 200ms 更省请求,比 500ms 体感更快。
3.3 XSS 防护
Markdown 渲染后的 HTML 直接插入 DOM,存在 XSS 风险。目前的处理:
- 文章标题用
escapeHtml()转义后插入 - 正文通过 marked 渲染(marked 默认不转义 HTML,但博客场景下作者即用户,风险可控)
如果开放多用户,必须引入 DOMPurify 做 HTML 消毒。
3.4 编辑页的实时预览
这是整个工具中体验最好的功能。布局用 CSS Grid 双栏:
.editor-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
左侧 Markdown 输入,右侧实时渲染。移动端自动切换为单栏(@media (max-width: 768px))。
3.5 Git 仓库管理
项目推送到 GitCode 仓库,使用 PRIVATE-TOKEN 认证。关键点:
.gitignore排除node_modules/- token 不硬编码在 remote URL 中(推送时临时使用,不持久化)
- 每次修改后
git add -A && git commit && git push三步走
四、存在问题与优化空间
4.1 当前问题
| 问题 | 严重程度 | 说明 |
|---|---|---|
| 无并发写入保护 | 中 | 多人同时写会丢数据,但个人博客场景下可忽略 |
| Markdown XSS 未消毒 | 中 | 单用户场景可控,多用户必须修 |
| 无图片上传 | 中 | 文章中只能用外链图片 |
| 无分页 | 低 | 文章超过 100 篇后列表加载变慢 |
| 搜索为线性扫描 | 低 | 文章量大时需加索引 |
| 无持久化会话 | 低 | 刷新页面后编辑内容丢失 |
4.2 优化路线图
短期(可用性提升):
- 编辑自动保存:
localStorage定时存草稿,刷新不丢内容 - 图片上传:支持拖拽上传到
public/uploads/,返回 URL - 文章分页:API 加
?page=1&limit=10参数
中期(功能增强):
- 全文搜索:引入 FlexSearch 做前端索引,支持中文分词
- 文章草稿与发布状态:增加
status: draft|published字段 - 导出功能:导出为 Markdown 文件包 / JSON 备份
长期(架构演进):
- 数据库迁移:当文章超过 500 篇,迁移到 SQLite(better-sqlite3)
- 多用户支持:加 JWT 认证 + DOMPurify 消毒
- SSR / SSG:用 Express 模板引擎做服务端渲染,SEO 友好
- Docker 部署:打包成镜像,一行
docker run启动
五、技术心得
心得一:轻量是一种选择,不是一种妥协
很多人觉得"不用框架"是技术退步。但在这个场景下,不引入框架恰恰是最合理的技术决策。3 个页面、6 个 API、1 个数据文件——引入 React + Redux + webpack 只会增加构建复杂度和维护成本,而不会带来任何体验提升。
工具的复杂度应该匹配问题的复杂度。
心得二:JSON 文件存储的哲学
"没有数据库"乍看是限制,实则是优势:
- 数据可读——打开
posts.json就能看到所有文章 - 便携——一个文件就是全部数据
- 可迁移——随时可以导入到任何数据库
这让我想到 SQLite 的设计哲学:“零配置、零维护、零管理员”。JSON 文件存储是更极端的版本——连二进制文件都不需要。
心得三:实时预览是写作体验的核心
用过 Markdown 编辑器的人都知道,实时预览是区分"好用"和"不好用"的分水岭。这个功能实现起来很简单——一个 input 事件 + marked.parse()——但对用户体验的提升是质变。
好产品的秘密:找到那些"实现成本低、体验提升大"的功能,优先做。
心得四:GitCode 仓库作为发布渠道
把博客工具本身发布到 GitCode 仓库,既版本管理又公开分享。这形成了一个有趣的闭环:
- 用博客工具写技术文章
- 把博客工具本身也放在仓库里
- 文章里分享的就是开发这个工具的经验
工具即内容,内容即工具。
六、总结
这个个人博客小工具用最朴素的技术栈(Node.js + Express + 原生前端 + JSON 文件),实现了一个完整可用的博客系统。它不是最强大的,不是最漂亮的,但它是最透明的——每一行代码你都能看懂,每一个数据你都能直接查看和修改。
有时候,最好的工具不是功能最多的,而是你完全掌控的。
本文由个人博客小工具的作者原创,首发于 GitCode 仓库 Eddygit/personal-blog-tool。
- 点赞
- 收藏
- 关注作者
评论(0)