引言:从「敢不敢动手」到「必须搞明白」
在 2025 年 4 月,我第一次真正接触到个人博客这一概念。
彼时的我,对技术始终抱有一种谨慎甚至畏惧的态度——在没有系统学习之前,不敢轻易尝试任何「看起来复杂」的东西。
为了尽快落地博客、减少未知风险,我选择了当时最稳妥、也最常见的一套方案:
Hexo + Butterfly 主题
它易上手、部署快、几乎零成本,看起来是一个「不会出错」的选择。
但也正是从这个看似简单的起点开始,我逐渐意识到:
工具的易用性,往往会掩盖其背后的复杂性,而真正的成长,往往发生在你被折磨得最狠的时候。
一、Hexo 初体验:简单背后的「隐性成本」
1. GitHub Pages 部署的现实问题
最初,我将 Hexo 博客部署在 GitHub Pages 上。
理论上流程很清晰:
本地修改配置或文章
执行
hexo generatehexo deploy推送到远程仓库
但现实却异常残酷。
由于 GitHub Pages 在国内访问需要「特殊网络环境」,整个部署过程充满了不确定性:
DNS 是否能解析,全看运气
SSL 证书配置耗费了大量时间
即使开启科学上网,对节点质量要求极高
到后期,甚至只有连接手机热点才能成功推送
hexo deploy是否成功,完全像在开盲盒。
2. 没有后台的「纯静态」管理体验
Hexo 最大的问题并不是部署,而是它没有后台管理系统。
这意味着:
文章创建、发布:命令行
图片、Logo 路径管理:手写配置
页面路由、站点信息、菜单结构:翻配置文件
页面结构与布局调整:直接改主题源码
每一次修改,几乎都要经历以下流程:
找配置项(可能在上千行文件里)
修改配置
清理缓存
重新生成静态资源
提交并推送
访问验证
如果配置错了——全部重来。
当时的我并不觉得这是问题,因为:
我还没有见过「真正的后台系统」
甚至连 Spring Boot 是什么都不知道
现在回头看,只能说一句:
一点细糠都没吃过。
二、三次关键改造:真正理解博客「原理」的开始
尽管困难重重,我还是对 Hexo 博客做了三次比较重要的改造。
这三次修改,反而成为我技术认知跃迁的起点。
1. 背景渐变:第一次真正「改源码」
Butterfly 主题默认的背景是纯白 / 纯黑,过于单调。我希望页面更有层次感,于是:
为主题引入了外部 CSS
实现了浅色 / 深色模式下的背景渐变
过程中遇到的问题包括:
页脚被原有样式遮挡 → 使用
z-index调整层级黑夜模式不适配 → 利用样式覆盖机制解决
最终的方案是:
浅色、深色样式同时渲染
根据模式切换,通过覆盖关系决定最终展示
这是我第一次意识到:
CSS 并不是「试出来的」,而是有明确规则和逻辑的。
2. 欢迎卡片:第一次理解「模板渲染」
Butterfly 提供了侧边栏卡片的扩展能力,但我想要的是:
在博客主页正文区域,新增一个固定欢迎卡片。
这意味着,我必须修改页面渲染模板。
在查阅主题源码后,我逐渐理解了博客的核心原理:
渲染模板 + 内容填充
我做了几件事:
新增一个模板文件
在模板中调用 IP、天气接口
根据访问者 IP、时间、天气动态输出欢迎信息
将该模板引入文章列表页模板中,并固定位置
那一刻我突然意识到:
只要你理解了模板结构,理论上你可以在任何位置插入任何功能。
3. 留言板:前后端分离的第一次实战
一个博客,怎么能没有留言系统?
Hexo 官方方案是使用第三方评论插件,但:
与博客本体割裂
与主题耦合度高
可定制性有限
我选择了另一条路:
利用 Hexo 支持 Markdown 中渲染 HTML 的能力
在md文件中直接嵌入前端页面
此时已经是 2025 年 7 月,我完成了 Spring Boot 的基础学习,于是顺理成章地:
前端:原生 HTML / CSS / JS(嵌入 md)
后端:Spring Boot
后端功能包括:
JWT 鉴权
基础 CRUD
权限校验
流量控制
异常处理
日志输出
后台管理系统使用 Vue 编写。
这个留言系统稳定运行了两三个月,直到服务器性能成为瓶颈。
三、从「折腾工具」到「选择框架」
Hexo 最大的问题始终是:没有后台。
我尝试过一些新技术栈的开源博客项目,但它们普遍存在问题:
生态不成熟
维护频率低
多为个人开发,稳定性存疑
我甚至一度产生了:
「自己写一个博客系统」的想法
但很快否定了:
这是在重复造轮子,成本极高,收益极低。
经过调研后,我选择了 Halo。
四、Halo:第一次「吃到细糠」
Halo 带来的体验几乎是降维打击:
完整后台管理系统
活跃社区与插件生态
专业团队维护
Docker 一键部署
部署流程极其简单:
拉取 Docker 镜像
使用 Docker Compose 组建
修改版本号即可升级
两天时间:
第一天理解整体逻辑
第二天完成博客迁移
文章、相册、资源几乎可以一键导入。
五、图库系统与 S3:一次「真实工程级」踩坑
在 Halo 稳定运行之后,我开始着手解决图库管理的问题。
通过阅读 Halo 官方文档以及部分开发者博客,我了解到一个成熟、独立的图库管理系统 —— ChronoFrame。
它并不与博客本体强耦合,而是作为一个独立服务存在,通过页面嵌入或跳转的方式进行访问,这种设计本身就非常符合工程直觉。
ChronoFrame 的部署过程几乎没有任何阻碍:
Docker 镜像拉取
基础配置文件填写
一键启动
真正的挑战,出现在 对象存储(S3)配置阶段。
1. 为什么一定要用对象存储?
在图库系统中,图片数量天然是不可控的:
照片体积大
访问频率高
下行流量消耗极快
如果全部使用服务器本地存储并通过服务器带宽直出:
带宽成本不可控
高峰期极易成为瓶颈
一旦迁移或重装,数据风险极高
因此:
将图片资源交给专业的对象存储服务,几乎是唯一理性的选择。
我最终选择的是 腾讯云 COS(S3 兼容协议)。
2. 问题现象:一半成功,一半失败
配置完成后测试,结果非常诡异:
✅ PUT:上传图片 正常
✅ DELETE:删除图片 正常
❌ GET:获取图片 失败
❌ POST:部分请求 失败
浏览器控制台统一报错为:
CORS 跨域问题
这一步,几乎是我被卡住时间最长的地方。
3. 第一轮误判:把问题全部归因于 CORS
看到跨域报错,我的第一反应非常「新手」:
一定是跨域规则没配好。
于是反复检查:
COS 跨域规则
方法是否包含 GET / PUT / POST / DELETE
是否允许全部来源
是否允许全部 Headers
但问题在于:
PUT 和 DELETE 明明是成功的。
如果跨域规则真的有问题,它不可能只对一半请求生效。
4. 交叉验证失败:阿里云尝试
为了验证是否是腾讯云的问题,我转而使用 阿里云 OSS。
结果更加离谱:
PUT 直接失败
报错为请求体解析异常
这一步让我意识到:
问题大概率不在云厂商,而在请求本身。
5. 关键转折:观察请求细节,而不是报错描述
在反复失败之后,我做了一件非常关键的事情:
逐条对比成功请求与失败请求。
6. 真正的原因:S3 Endpoint 路径风格错误
(1)两种 S3 访问风格
Virtual Hosted–Style
https://<bucket>.<endpoint>/<object>Path–Style
https://<endpoint>/<bucket>/<object>
腾讯云 COS 只支持 Virtual Hosted–Style。
(2)PUT 与 GET 使用了不同风格
PUT 使用:Virtual Hosted–Style(正确)
GET 使用:Path–Style(错误)
浏览器手动访问时,腾讯云直接返回 XML 错误,明确提示:
当前访问路径不符合 Virtual Hosted Style。
7. 为什么它会「伪装成跨域问题」?
原因在于:
请求被直接拒绝
响应头中 没有 CORS Header
浏览器统一判定为跨域错误
这是一个「路径错误 → 响应头缺失 → 被误判为 CORS」的经典陷阱。
8. 最终解决方案:统一访问域名
在不修改 ChronoFrame 源码的前提下,最稳妥的方案是:
不要让 CDN 配置为空
在 CDN 字段中填写合法访问域名
可选方案:
https://<bucket>.<endpoint>绑定自定义域名(推荐):
https://img.example.com
ChronoFrame 会优先使用该域名生成访问 URL,从而绕过错误路径拼接。
9. 最终效果与收益
云端保存 原图
访问时返回 压缩图
下行流量成本大幅降低
当前图库:
图片数量:100+
原始体积:约 700MB
如果原图直出,成本会非常可怕;开启压缩后,几乎可以忽略。
结语:博客不是目的,而是放大器
回头看这一路:
Hexo 教会我阅读源码
模板修改让我理解渲染逻辑
留言系统让我第一次完整跑通前后端
Halo 让我真正理解「框架的价值」
博客本身不是目的,它只是一个放大器:
放大你的短板
放大你的好奇心
也最终放大你的成长
这也是我写下这篇复盘的原因。