这个博客是什么
如果你看到这篇,八成是被某个搜索引擎或者朋友拉过来的 —— 欢迎。
这是我自己折腾出来的一个纯手搓博客:从前端到后端、从数据库到部署、从代码高亮到访问日志,全是自己拼起来的。
技术栈很普通,选型基本是"哪个我用得顺手就用哪个":
- 前端 / SSR:Nuxt 4 + Vue 3 + TypeScript + Tailwind CSS 3
- 后端:Nitro(Nuxt 自带)+ MySQL(
mysql2/promise) - 认证:JWT(
jose)+bcryptjs,Cookie HttpOnly - 服务器:阿里云 ECS 2C2G + Rocky Linux 9.5,PM2 cluster + Nginx 反代
- 部署:本地构建 → tar → scp → SSH →
pm2 reload,一条pnpm run deploy搞定
一开始用过 Hexo 和 Hugo,甚至想过 WordPress,最后还是觉得自己写一个最自由:想加什么功能加什么,不用受主题约束,而且顺便还能练手。
它现在能干嘛
除了"显示文章"这个基本功能,这一年陆陆续续给它加了不少东西:
- 📝 后台编辑器:Markdown textarea + 实时预览,500ms 自动写 localStorage 草稿,断电不丢稿
- 🔐 完整的 RBAC 后台:用户 / 角色 / 权限点 / 动态菜单,后期想多账号协作直接加
- 📊 数据分析:ECharts 出图,看哪天 PV 高、哪篇文章被分享了
- 🔍 访问日志:每个请求一行,IP / UA / 路径 / Query / Body / 状态码 / 耗时 / 设备 / 浏览器 / 30+ 种爬虫识别,密码 token 自动脱敏
- 🛠️ 在线工具集 /tools:15 个开发者小工具,JSON / Base64 / 时间戳 / 正则 / 颜色 / 哈希 / UUID / JWT / 字数 / 密码生成 / 二维码 / AES / 图片上传 / JS 压缩混淆... 大部分纯前端,数据不上传
- 🖼️ 自带图床:
/admin/uploads后台直接拖图,SHA256 命名,Markdown 一键复制粘贴到文章 - 💬 评论:Giscus,基于 GitHub Discussions,垃圾评论自动挡,我也不用维护数据库
- ⚡ 性能优化:文章 HTML 进程内 LRU 缓存,shiki 双主题代码高亮预渲染,长文有 sticky TOC
- 🤖 SEO:
/sitemap.xml//rss.xml/ OG meta / Twitter Card / JSON-LD / canonical 全套
几段实际的代码
文章详情接口里,渲染好的 HTML 会进进程内 LRU 缓存,避免每次请求都去 marked + shiki 跑一遍:
const cache = new Map<string, { html: string; expiresAt: number }>()
const TTL_MS = 5 * 60 * 1000
export async function getRenderedPost(slug: string) {
const hit = cache.get(slug)
if (hit && hit.expiresAt > Date.now()) return hit.html
const md = await fetchPostMarkdown(slug)
const html = await renderMarkdown(md) // marked + shiki 双主题
cache.set(slug, { html, expiresAt: Date.now() + TTL_MS })
return html
}数据库改 schema 不用写独立的 migration 文件,直接在 server/utils/db.ts 里追加 ALTER,启动时幂等执行:
ALTER TABLE access_logs
ADD COLUMN request_body VARCHAR(2048) NULL AFTER query_string;
ALTER TABLE posts
ADD FULLTEXT INDEX ft_posts_search (title, description, content)
WITH PARSER ngram;失败静默 —— 列已经存在就跳过,从不阻塞启动。这样代码版本和 schema 版本永远在一起走。
为什么不用现成的轮子
工具和自己造,一直是个取舍。我的逻辑大概是这样:
| 选项 | 优点 | 我为啥放弃 |
|---|---|---|
| Hexo / Hugo | 静态生成快、托管 0 成本 | 没有后台,改一篇文章都得本地编辑 + git push |
| WordPress | 生态最全、有图床有评论 | PHP + MySQL + 各种插件,光维护就够头疼 |
| Vercel / Netlify Hosting | 免运维、自动 HTTPS | 想加自定义后端(图床 / 工具集 API)有限制,流量起来了也不便宜 |
| 自己写 + 阿里云 ECS | 完全可控,想加啥加啥 | 要自己运维,但反正我也是写后端的 |
说白了,这博客不只是博客,也是我的练兵场。访问日志、限流、暴力破解防护、PM2 cluster 调优、shiki 集成、Rollup external、Nginx 反代、Nitro 静态文件运行时读盘... 每个功能都让我多懂了一点真实世界的工程问题。
写到这里突然想起一个真实踩过的坑:
javascript-obfuscator这个包的dist/index.js是 webpack 预打包的复杂 CJS,Rollup 的 commonjs 插件静态分析它会直接PARSE_ERROR。
解法是nuxt.config.ts的nitro.externals.external+nitro.rollupConfig.external双保险标记 external,业务里用await import()延迟加载,运行时由 Node 标准 module resolution 从node_modules/找。这种小坑全网搜不到几句中文资料,以后会单独写一篇详细聊。
接下来打算写什么
大方向是技术,但具体的话,目前在脑子里排队的有这么几类:
- 真实踩坑 —— 工作或业余项目里遇到的怪问题,记录下来给后人省时间
- 小工具开发笔记 —— 每加一个
/tools/<xxx>,顺手写一篇"它怎么实现的" - 运维笔记 —— 阿里云上的小服务器怎么省钱用 + 不被 OOM,从 1Panel 的坑到 PM2 cluster 的内存账
- 零散思考 —— 看了什么书、听了什么播客、写代码时的小感悟
不许愿,先跑起来。
欢迎在下面评论区(用 GitHub 账号登录)留几句,或者去 归档 / 分类 / 标签 翻翻看有没有感兴趣的。
下次见。