这次重新折腾博客,并不是从“我想换一个主题”开始的,而是从一个看起来很普通、实际上让我排查了很久的问题开始的。
当时我只是进入博客目录,执行了一条以前经常使用的命令:
hexo s
命令执行之后,本地服务能够启动,浏览器也能够打开本地地址,但是页面一直停留在加载状态,博客正文、文章列表和侧栏都没有正常显示。刚开始我以为只是图床加载比较慢,或者是 Live2D 看板娘的资源没有加载出来,但等了很久之后,页面仍然没有恢复。

现在回头看,这次故障最重要的地方并不是最后改了哪一行代码,而是让我第一次比较完整地意识到:Hexo 服务启动成功,并不等于浏览器中的页面运行成功。
# 一、先确认问题到底发生在哪里
遇到博客打不开时,最容易做的事情是重新安装依赖、换一个主题,或者把生成目录全部删除。但这些操作有一个共同的问题:它们都没有回答“错误究竟发生在什么位置”。
所以我先把整个过程拆成了几层:
Hexo 服务是否启动
↓
静态 HTML 是否生成
↓
主题 CSS 和 JavaScript 是否加载
↓
浏览器脚本是否能够执行
↓
页面初始化逻辑是否完成
首先检查终端。hexo s 并没有立即退出,4000 端口也能够访问,这说明 Hexo 服务本身并不是完全没有启动。
接下来我打开浏览器开发者工具,终于看到了真正有用的线索:
Uncaught SyntaxError: Identifier 't' has already been declared
(at app.js?v=0.2.5:1:1)
这类错误和“网络慢”没有关系。浏览器在解析 JavaScript 的阶段就已经失败了,同一个作用域内出现了重复声明的变量,后续依赖这段脚本执行的初始化代码也就不会继续运行。页面中那个一直显示的加载框,本质上只是一个还没有机会被替换掉的初始状态。
# 二、为什么终端没有报错,页面却不能显示
Hexo 的工作方式很容易让人产生误判。Hexo 负责读取 Markdown、调用渲染器并输出 HTML,浏览器拿到这些 HTML 之后,还要继续加载主题的 CSS、JavaScript 和各种第三方资源。
因此一个页面可能处于下面这种状态:
- HTML 已经生成。
- HTTP 服务已经启动。
- 浏览器已经请求到了页面。
- 但主题 JavaScript 在解析阶段失败。
- 依赖 JavaScript 的菜单、加载器、PJAX 和评论初始化没有执行。
这也是为什么我后来不再把“终端没有 FATAL”当作页面正常的充分条件。终端负责告诉我构建和服务端做了什么,浏览器控制台则告诉我页面在客户端做了什么,两边的信息必须结合起来看。
# 三、逐项隔离主题和附加功能
旧项目中除了 Shoka 主题本身,还加入了 Live2D 看板娘、播放器、图床和其他脚本。它们都可能在布局文件中插入 JavaScript,所以我没有一上来就认定一定是主题本身的问题,而是先做隔离。
# 1. 检查生成后的 public 目录
首先确认首页和文章页的 HTML 文件是否存在,再查看页面中是否出现了重复的脚本引用。如果 HTML 根本没有生成,排查重点应该放在 Hexo 渲染器、文章 Front Matter 和主题模板;如果 HTML 内容完整但页面不动,才应该把重点放到资源和浏览器脚本上。
# 2. 检查主题布局中的脚本
我重点检查了主题公共布局和 head、footer 相关文件,主要看以下几件事:
- 同一个
app.js是否被引用了两次。 - 主题原本的脚本和手动添加的脚本是否重复。
- Live2D 是否同时加载了本地脚本和 CDN 脚本。
- 生成后的页面是否仍然保留旧版本的资源路径。
这里有一个很容易忽略的地方:浏览器报错中显示的是生成页面里最终加载的资源,不一定就是你正在编辑的那个源文件。主题源文件、Hexo 生成目录和浏览器缓存之间如果没有区分清楚,修改很容易变成“改了一个文件,但页面仍然加载另一个文件”。
# 3. 暂时关闭 Live2D 等附加脚本
看板娘功能本身并不是博客正文显示的必要条件,所以我把它暂时当作一个独立变量处理。先关闭附加脚本,再清理生成目录并重新启动博客。如果主题主体恢复正常,再逐项恢复 Live2D、播放器和其他功能,就可以判断是哪个功能引入了新的冲突。
这个过程看起来比较慢,但比一次性修改多个文件更容易得到确定结论。前端问题最怕“同时改了五处,然后页面突然好了”,因为下一次再出问题时,自己也不知道真正起作用的是哪一处。
# 四、为什么没有直接删除旧项目重装
旧博客里已经积累了很多不能轻易丢掉的内容:
- 文章 Markdown 文件。
- 分类、标签和文章路径。
- GitHub 图床直链。
- 主题背景和页脚配置。
- Live2D 本体、API 和模型文件。
- Algolia 搜索配置。
- GitHub Pages 部署配置。
如果只是为了处理一个页面加载错误,就把整个目录删除重新开始,表面上可能比较干净,实际上会把问题扩大成一次完整迁移。文章可以复制,但图片路径、评论、搜索索引和主题细节都需要重新处理。
所以我后来采用了更稳妥的方式:保留原来的 now 项目,另外复制出测试项目进行排查。原项目作为回退,测试项目负责实验,任何不确定的改动都不直接覆盖原始环境。
# 五、重新建立一个可以验证的构建基线
当问题逐渐从“页面完全打不开”缩小到“主题资源和附加脚本的兼容性”之后,我开始重新建立构建基线。
首先清理旧生成文件:
hexo clean
然后重新生成:
hexo generate
最后启动本地预览:
hexo server
我把“构建成功”定义得比命令返回成功更严格一些:
public/index.html确实生成。- 文章、分类、标签和归档页面都存在。
- 首页和文章页可以正常打开。
- 浏览器控制台没有致命 JavaScript 错误。
- 暂时关闭附加功能时,博客主体仍然可以显示。
- 恢复附加功能后,能够确定每一个功能的影响范围。
这样的验证方式比单纯看终端最后有没有红字可靠得多。尤其是静态博客,构建、服务端和浏览器其实是三套不同的运行环节。
# 六、这次故障给我的几个启发
第一,浏览器控制台往往比终端更接近页面真正的故障原因。终端告诉你 Hexo 有没有启动,控制台告诉你主题脚本有没有运行,这两类信息不能互相替代。
第二,遇到问题时要先分层,再修改。服务端、生成结果、主题资源和第三方脚本分别检查,排查速度反而会更快。
第三,博客项目不能只看成一堆文章。主题、插件、图片、评论、搜索和部署配置共同组成了博客,任何一层出问题,都可能让最终页面看起来像是“整个博客坏了”。
第四,备份并不是保守,而是提高试错效率。如果知道随时可以回到旧版本,就不会因为害怕破坏环境而不敢尝试,也不会在失败后只能从头重建。
最后,这次问题让我开始认真思考一个更大的问题:如果每次改文章、上传图片、更新搜索和部署都要手动记住一串命令,那么博客维护本身就会变成新的负担。也正因为如此,我才开始考虑迁移到更现代的 ShokaX,并把后续的维护流程整理成一个可以操作的管理台。
这也就引出了下一篇文章:如何从 Shoka 迁移到 ShokaX,以及如何把一个需要反复敲命令的 Hexo 博客,逐步整理成一个可视化管理系统。