从 Wagtail 迁移到 Astro(二):迁移的几个阶段
本文是「Wagtail 迁移 Astro」系列的第二篇,说明迁移工作拆成的几个阶段,以及每个阶段要完成的事情。
一、整体阶段划分
迁移可以拆成四个主要阶段,顺序建议如下:
阶段 1:数据与资源同步 → 阶段 2:内容转换与导入 → 阶段 3:站点结构与脚本化 → 阶段 4:验证与切换
下面按阶段说明。
二、阶段 1:数据与资源同步
目标:把线上 Wagtail 的「数据」和「媒体资源」拿到本地,且可重复执行(便于后续增量更新)。
2.1 需要同步的内容
- 数据库:Wagtail 使用的 SQLite(如
zicodedb),包含文章、分类、标签、专栏、StreamField 等。 - 媒体文件:上传的图片等,通常位于
media/original_images/或类似路径。 - 必要代码/配置:若需本地用 Django 跑一遍导出逻辑,可同步
manage.py、requirements.txt、相关 app 与模板。
2.2 同步方式建议
- 使用 SSH + 远程命令(如
scp、rsync)从服务器拉取:- 数据库文件整份拷贝到本地
legacy-wagtail/或类似目录。 - 媒体目录按需 rsync,避免重复拉取大文件。
- 数据库文件整份拷贝到本地
- 在项目里写一个 同步脚本(如
scripts/sync_legacy.py),用配置项记录主机、路径、本地目录,以后一键执行,便于增量更新。
同步完成后,本地应具备:一份可读的 SQLite、一份与线上一致的媒体文件、以及(如需要)可运行的 Wagtail 项目副本。
三、阶段 2:内容转换与导入
目标:把 Wagtail 里的文章变成 Astro 的 Markdown + frontmatter,并处理好图片路径。
3.1 内容模型对应关系
| Wagtail 概念 | Astro 对应 |
|---|---|
| 文章标题、摘要、正文 | title, description, Markdown body |
| 发布时间、更新时间 | pubDate, updatedDate |
| 分类、专栏 | category, column(frontmatter 字符串) |
| 标签 | tags(frontmatter 字符串数组) |
| 作者 | authors(frontmatter 字符串数组) |
| 头图/特色图 | heroImage(相对路径或 URL) |
| StreamField 块 | 解析为 Markdown:段落、标题、列表、代码块、图片块等 |
3.2 正文转换要点
Wagtail 的 StreamField 存的是 JSON 数组,每个元素有 type 和 value。常见块类型与转换方式如下:
| 块类型 | 典型 value | 转为 Markdown 的方式 |
|---|---|---|
paragraph | { "text": "..." } | 直接输出为一段,前后空行 |
markdown | { "source": "..." } | 原样输出 source,必要时包在代码块外 |
heading | { "text": "标题", "level": 2 } | 输出 ## 标题(按 level 写 # 个数) |
image | { "image": id 或 path } | 查媒体表得到路径,输出  |
code | { "language": "python", "code": "..." } | 输出 ```language\n...\n``` |
list | { "items": [...] } | 遍历 items 输出 - item 或有序列表 |
注意:图片块需要根据 Wagtail 的 media 表或文件路径解析出实际文件名,并统一写成站内路径(如 /uploads/original_images/xxx.png),导入时把文件拷贝到 public/uploads/original_images/。若原有内容已是 Markdown 块,可尽量保留原样,只做换行与块类型包装上的统一;避免对 Markdown 做二次转义导致代码块或链接错乱。
3.3 导入脚本
- 写一个 导入脚本(如
scripts/import_wagtail.py):- 连接本地 SQLite,读取文章表与相关字段。
- 按上述规则生成 frontmatter + Markdown 正文。
- 将每篇文章写入
src/content/blog/imported/xxx.md。 - 可选:同时拷贝用到的图片到
public/uploads/original_images/。
- 建议为每篇导入文章保留 legacyId(Wagtail 主键),便于以后对账或重跑部分导入。
完成本阶段后,应能在 Astro 里通过 getCollection('blog') 看到全部历史文章,且排版、链接、图片正常。
常见问题:
- 图片 404:检查导入脚本里图片目标路径是否与 frontmatter 中的
heroImage、正文里的一致,且文件确实拷贝到了public/下。 - 中文 slug 乱码:Astro 路由若用中文,需统一用
encodeURIComponent或自建 slug(如拼音),并在导航、面包屑里一致使用,避免一半编码一半不编码。
四、阶段 3:站点结构与脚本化
目标:确定 URL 结构、导航、专栏/分类/标签页,并把「同步 + 导入」固化为一键流程。
4.1 URL 设计
- 文章页:
/blog/<slug>/,与 Wagtail 的 slug 或自定义 slug 一致,便于做 301 重定向(若需要)。 - 分类 / 专栏 / 标签:
/blog/category/<name>/、/blog/column/<name>/、/blog/tag/<name>/,对中文名做encodeURIComponent或统一 slug 规则。
4.2 导航与数据
- 在 Astro 中通过
getCollection计算分类、专栏、标签列表,供 Header、Footer、侧栏使用。 - 专栏名、分类名与 Wagtail 保持一致,这样内部链接和读者习惯都不会乱。
4.3 脚本化
- sync:从服务器拉取最新数据库与媒体(如
python scripts/sync_legacy.py)。 - import:读取本地 SQLite,覆盖或增量写入
src/content/blog/imported/(注意不要误删手写的新文章)。 - 可选:在 CI 或本地 Makefile 中串联
sync → import → astro build,实现「从旧站拉数据 → 重新导入 → 构建」一条龙。
五、阶段 4:验证与切换
目标:确认迁移结果正确,再切换线上流量或与现有站点并存。
5.1 内容校验
- 文章数量、标题、日期与 Wagtail 后台或导出列表一致。
- 抽检若干篇文章:正文、代码块、图片、内链是否正常。
- 分类、专栏、标签列表是否完整,链接是否可访问。
5.2 构建与部署
- 在本地执行
astro build,检查无报错、无错误链接。 - 将
dist/部署到测试域名或子路径,用浏览器与简单爬虫过一遍关键路径。
5.3 切换策略
- 直接切换:将 Astro 构建结果部署到原域名根路径,旧 Wagtail 下线或仅保留后台只读。
- 并行一段时间:Astro 站用新子域或新路径,旧站保留,再择机切主域并做 301。
完成以上四阶段,迁移的主干就打通了。
阶段小结:先「拿到数据」再「转成 Markdown」再「定 URL 与脚本」最后「验证再切换」,每一步都可单独回滚或重跑,建议每阶段做完都在本地 pnpm build 与 pnpm preview 跑一遍,再进入下一阶段。
本系列文章:(一)为什么迁移与选型 · (二)迁移的几个阶段(本文) · (三)添加新文章 · (四)上线 · (五)数据同步与脚本化 · (六)主题与功能打磨