把博客变成静态文件,部署确实简单了。但读者收藏的旧链接、正文里的截图、订阅器里的地址,都不关心我换了什么生成器。只把首页跑起来,离迁移完成还差很远。

这次本机使用的是已经从 Typecho 整理出来的 Hugo 内容仓库,并没有直接对线上 PHP 数据库做切换。文章的数字 ID、分类名称和旧地址仍然继承自 Typecho;现在要做的,是让这些内容进入 Hexo 的构建流程,同时保住原来的阅读方式。

先保地址,再换生成器

我没有让标题决定迁移文章的地址。标题以后可能会改,旧文章的数字 ID 不应该跟着变。每篇文章用 slug 保存数字 ID,并显式指定固定链接,例如本文:

slug: '2961'
permalink: /archives/2961/

这样迁移后的地址仍然是 /archives/数字ID/,不会突然多出日期目录或标题拼音。原来的发布时间映射到 date,最后修改时间映射到 updated,旧数据里的时区也一并保留,不拿复制文件时的时间重排历史文章。

独立页同样有自己的约定:关于页保留 /about.html,时间线是 /update.html,归档是 /custom.html,搜索仍在 /search/。首页、/blog/、分类和标签的分页,也要逐一核对,不能只验证第一页。文章内部的标题锚点则单独检查,地址没变,不代表带 #片段 的链接一定还能准确跳转。

资源与分类,要保留原来的含义

图片、音频和视频继续沿用 /usr/ 下的原路径。先让引用能对应到实际文件,再谈压缩和格式优化,避免迁移时同时改文件名、路径与图片内容,出了问题连原因都不好找。旧站域名下的正文链接转成相同的站内路径,外链和代码示例里的地址保持原样。

分类也不是简单地改一个字段名。Hugo 中的一组分类表示独立归属,而 Hexo 的普通分类数组可以表达父子关系。为了避免“笔记”和“折腾”意外变成上下级,我把独立分类写成分开的分支:

categories:
  - [笔记]
  - [折腾]

分类和标签的展示名从原来的 _index.md 读取,URL 则恢复原 slug。这样“数据结构”可以继续走历史上的 data_structures 地址,显示名与链接规则各负其责。原站未被文章使用的标签入口,也需要有明确处理,不能因为模型里暂时没有文章就悄悄消失。

短代码和代码块,不能只看“没有报错”

Typecho、Hugo 和 Hexo 的处理顺序不同。同一段文字,可能先经过 Markdown,再被短代码引擎解释;括号、强调、表格和多行属性一旦碰在一起,页面能生成,内容却可能已经变了。

这批 Hugo 提示框、收缩框和媒体标签已经转换成 Waxy 的方括号语法,并在构建时展开。标签保护、嵌套与转义统一由主题处理,不再在站点根目录维护另一份短代码适配。围栏代码和行内代码里的标签只是示例,必须保留为文字,HTML 属性里的方括号也不能被当成正文执行。旧文章中已有的错误标签,则单独记录修复,不把错误包装成兼容特性。

代码高亮交给 Hexo 内置 Prism,在构建时生成高亮与行号;浏览器保留复制和换行操作,不再重新跑一遍高亮器。旧代码块的语言标记已直接规范为 apacheconf、c、javascript 等原生名称,不再额外维护转换脚本。验收时还要比较代码正文、TAB 和换行,不能因为出现了彩色的 span 就算通过。

同一篇文章会出现在多个列表中,短代码、目录和公共统计还需要避免反复计算。本机实现把可复用结果限制在单次构建内,每次重新生成都会清理缓存,防止 watch 模式下改了正文,却继续读到旧结果。

评论、搜索与订阅,分别安排出口

评论完全交给外挂。主题保留 giscus、Waline、Twikoo 的接入能力,当前站点的 provider 是 none,不会生成一个貌似能提交、实际没有后端的评论框。旧评论如果要保留,需要另做评论服务的数据迁移,不能把旧统计数字搬过来就算迁完。

搜索使用构建期生成的 search.json,浏览器读取索引并筛选结果,入口仍兼容 /search/?q=关键词。RSS 生成在 /feed.xml,同时保留 /feed 的兼容入口;站点地图则生成 XML 和文本版本。这些都是独立的内容出口,更新文章后应随站点一起重建,而不是只替换一张文章页。

私有草稿单独放在 _drafts,并明确标为不发布。检查时也要同时看 HTML、搜索、RSS 和 sitemap,确认它们没有混进公开结果。关于静态内容与保密的取舍,我另写在了加密功能的复盘里。

从构建成功,走到可以交付

当前工程的基本命令很直接:

pnpm clean
pnpm build
pnpm test

但我不会只看命令最后有没有报错。自动检查负责核对预期路由、公开文章与草稿边界、静态资源、canonical 和内容残留;浏览器检查负责看实际页面中的代码复制、换行、目录、灯箱和手机导航是否能用。两种证据各有用途,生成了一份文件,不能替代真实交互。

迁移前后的资源与内容清单也要留下。它们既能帮助找回漏掉的图片,也能区分旧数据本来存在的问题与这次转换引入的问题。迁移完成后,一次性工具和验收记录已移到项目之外,日常工程只维护内容、主题和配置;旧入口的路由数据留在 source/_data/,由主题按需读取。正式发布仍是另一件事,不能把本地构建通过写成已经完成线上切换。

静态化减少了公开站点对 PHP、数据库和后台运行环境的依赖,维护工作则转移到构建脚本、依赖和发布过程。对这个以文章为主的博客,我更愿意接受这个取舍:把可重复的转换和检查放在构建期,把读者真正需要的阅读体验留在页面里。

以上流程按 2026-10-27 的本机代码核对,描述的是当前工程的实现和验收方法。

参考