视频加载失败

从Hexo迁移到Astro部署记录

3258 字
16 分钟
从Hexo迁移到Astro部署记录

一、为什么从 Hexo 迁移到 Astro#

原来这个博客是用 Hexo 搭的(另一篇《学习用Hexo写博客》记录了完整过程),部署在 GitHub Pages 上,用起来没问题。但用久了有几个痛点:

  • 主题老旧:Hexo 生态里好看的现代主题不多,定制麻烦;
  • 性能一般:页面是整站生成,JS 和 CSS 越来越重;
  • 想折腾:Astro 是当下内容站的主流方案,静态输出、组件化、生态活跃。

最终选型:

项目选择理由
框架Astro 7内容驱动、默认零 JS、构建快
主题Firefly基于 Fuwari 二次开发,二次元风格、功能全
托管GitHub Pages免费、和仓库联动
部署GitHub Actionspush 即自动构建发布

二、搭建项目与本地预览#

前置环境:需要 Node.js 20+(去 nodejs.org 下载安装)和 pnpm 包管理器:

Terminal window
# 安装 pnpm(Node.js 自带 corepack,也可用 npm 装)
npm install -g pnpm
pnpm --version # 确认安装成功

Firefly 主题的仓库地址:CuteLeaf/Firefly。克隆后安装依赖:

Terminal window
# 克隆模板(我的目录是 my-firefly-blog)
git clone https://github.com/CuteLeaf/Firefly.git my-firefly-blog
cd my-firefly-blog
# 安装依赖并启动本地预览
pnpm install
pnpm dev

浏览器打开 http://localhost:4321 即可看到站点。之后每次改配置,本地跑 pnpm build + pnpm preview 验证,确认无误再推送。

三、双站并存:旧博客不丢#

迁移期间新站还没完全就绪,旧站不能直接关。方案是双站并存

  • 新站(Astro):部署在 lycaonide.github.io 仓库,即主站 https://lycaonide.github.io
  • 旧站(Hexo):保留原内容,作为项目页部署到 https://lycaonide.github.io/hexo-blog/

等新站内容补齐后,再逐步把旧站文章迁过来,最后下线旧站即可。迁移过程零停服,随时可以回滚。

四、字体本地化(最大的坑:jsdelivr 被墙)#

这是迁移过程中最折腾的一环。主题默认从 jsdelivr CDN 下载 3 个在线字体(Zen Maru Gothic / Inter / JetBrains Mono),而 jsdelivr 在国内经常被墙,导致:

  • 构建卡在字体下载、超时失败;
  • 线上页面字体加载不出来,回退成默认字体,很丑。

试过的方案和结论:

方案结果
全局字体改系统字体横幅标题、代码块仍引用在线字体
把字体装成 npm 包,provider 改 npmAstro 7 的 npm provider 依然走 jsdelivr
local provider集成静默失效,复制了字体但没生成 @font-face
手动 @font-face + 本地 woff2成功,完全离线构建

最终做法:

  1. 把 4 个 woff2 字体文件放进 public/assets/fonts/(字体可从主题自带的 assets 目录复制,或从 Google Fonts 下载对应字重的 woff2);
  2. 新建 src/styles/local-fonts.css,手写 @font-face
src/styles/local-fonts.css
@font-face {
font-family: "Noto Sans SC";
src: url("/_astro/fonts/2ae3372f6bc2f1d5.woff2") format("woff2");
font-weight: 500;
font-display: swap;
}
@font-face {
font-family: "Space Grotesk";
src: url("/_astro/fonts/6c826b4054a55260.woff2") format("woff2");
font-weight: 500;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("/_astro/fonts/0b2768d3228f0111.woff2") format("woff2");
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "JetBrains Mono";
src: url("/_astro/fonts/0a0e0606aec0d99b.woff2") format("woff2");
font-weight: 700;
font-display: swap;
}
  1. fontConfig.ts 里把 selected 设为 ["system"],让全局走系统字体;
  2. 写一个子集化脚本:扫描构建产物里的所有 HTML,收集实际用到的字符,只保留这些字符生成轻量 woff2(核心代码用了 subset-font 库):
// scripts/subset-fonts.ts(核心逻辑)
import subsetFont from "subset-font";
import { glob } from "glob";
import { readFile, writeFile } from "node:fs/promises";
// 1. 收集 dist/ 里所有 HTML 的实际字符
const htmlFiles = await glob(`${DIST_DIR}/**/*.html`);
const charSet = new Set<string>();
for (const file of htmlFiles) {
const html = await readFile(file, "utf-8");
for (const c of html.replace(/<[^>]+>/g, " ")) charSet.add(c);
}
// 2. 用 subset-font 生成子集 woff2,再替换 CSS/HTML 里的引用
const subset = await subsetFont(fontBuffer, [...charSet].join(""), {
targetFormat: "woff2",
});
await writeFile(outFile, subset);

这个脚本已集成进 pnpm build(构建链会自动执行),也可以单独跑:npx tsx scripts/subset-fonts.ts。结果:字体体积从 3MB 压到约 140KB(换了思源黑体后更小),页面加载快了很多。

备选:在线字体(简单,但访客需要能连上 jsdelivr)#

想省事的话,也可以用 Fontsource + jsdelivr CDN 在线引入,主题用的三个字体几行 CSS 就能加:

/* 放在全局样式入口文件顶部 */
@import url("https://cdn.jsdelivr.net/npm/@fontsource/zen-maru-gothic@5/index.css");
@import url("https://cdn.jsdelivr.net/npm/@fontsource/jetbrains-mono@5/index.css");
@import url("https://cdn.jsdelivr.net/npm/@fontsource/inter@5/index.css");

然后在字体配置里直接引用对应字体名即可,不用下载文件、不用子集化。

但要注意在线方案的工作方式:字体不是打包进站点的,而是访客打开页面时由浏览器现场去 cdn.jsdelivr.net 拉取——访客网络连不上这个域名(国内直连常被墙)时,字体就加载不出来,页面回退成系统字体。它省的是站长的事,赌的是访客的网络。

本站最终用的是本地化方案(上面这套,已实测通过):字体文件存在站点自己的服务器上,访客打开本站不需要连任何外部字体服务,构建和线上都稳。

五、站点美化:装饰总开关#

主题本身是二次元风格,加了几个装饰效果:樱花飘落、水波纹背景、卡片立体感。为了让它们可管理,我统一收敛到一个配置文件 src/config/decorationConfig.ts

src/config/decorationConfig.ts
export const decorationConfig = {
sakura: true, // 樱花飘落
live2d: true, // 看板娘
waves: true, // 背景水波纹
card3d: true, // 卡片立体感
};

想换”极简技术风”时,全部改成 false 就行,不用到处找开关。

六、评论系统:Giscus#

评论用的是 Giscus(基于 GitHub Discussions,免费、无广告、数据在自己仓库里)。接入步骤:

  1. 开启 Discussions:仓库 Settings → Features → Discussions 勾选开启。开启后仓库导航栏会出现 Discussions 入口:

步骤1:开启仓库 Discussions
步骤1:开启仓库 Discussions

  1. 安装 giscus Appgiscus.app → Install,授权给博客仓库;
  2. 生成配置:在 giscus 配置页填仓库名,页面会校验仓库状态(必须公开、已装 App、已开 Discussions),三项都满足会显示”成功”:

步骤3:giscus 配置页校验仓库
步骤3:giscus 配置页校验仓库

  1. 写入主题配置 src/config/commentConfig.ts
src/config/commentConfig.ts
export const commentConfig = {
type: "giscus",
repo: "lycaonide/lycaonide.github.io",
repoId: "R_kgDORApdOw",
category: "Announcements",
categoryId: "DIC_kwDORApdO84DFWdq",
mapping: "title", // 按文章标题匹配讨论
reactionsEnabled: "1",
inputPosition: "top",
lang: "zh-CN",
loading: "lazy",
};

补充:分类我选了 Announcements(公告分类),可以防止陌生人随意开新讨论刷屏。

七、友链页面#

主题自带了友链页,只是默认关闭。开启方法:

src/config/siteConfig.ts
friends: true, // 原来是 false

同时新建 src/content/spec/friends.md 写入友链说明(本站信息、申请方式、小要求),页面底部就会显示自定义的友链交换说明。

注意:友链内容页缺失会导致构建报错 friends page content not found,所以 开关和内容文件要一起建

八、GitHub Actions 自动部署#

推送后自动构建发布,用的是 Actions workflow(.github/workflows/deploy.yml),这是本站实际在用的完整配置:

name: Deploy to GitHub Pages
on:
push:
branches: [ main, master ]
workflow_dispatch:
# Pages 部署需要的权限
permissions:
contents: read
pages: write
id-token: write
# 确保只有一个部署任务同时运行
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.22.0
run_install: false
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Build site
run: pnpm run build
- name: Create .nojekyll file
run: touch dist/.nojekyll
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: dist # Astro默认构建输出目录
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

几个关键点:

  • touch dist/.nojekyll:GitHub Pages 默认用 Jekyll 处理,不建这个文件会把 _astro 这类目录忽略掉,页面会白屏;
  • concurrency:防止多次 push 时部署任务互相打架;
  • workflow_dispatch:想手动触发重新部署时,Actions 页面点一下即可。

每次 push 后,仓库的 Actions 页面会看到构建部署记录(绿色对勾表示成功,点进去能看每步日志):

步骤:push 后在 Actions 页面查看部署记录
步骤:push 后在 Actions 页面查看部署记录

以后写文章只需要:

Terminal window
git add .
git commit -m "新文章"
git push

前置:git push 走 SSH,需要先在 GitHub 配置好 SSH 密钥(生成密钥、把公钥加到 GitHub → SSH and GPG keys,方法可参考本站《学习用Hexo写博客》一文第五节)。

等待 3-4 分钟,Actions 构建部署完成,线上自动更新,全程不用手动操作。

十、Cloudflare Pages 部署(国内访问加速)#

GitHub Pages 的服务器在境外,国内访问时快时慢,图片、字体偶尔要等很久。为了让国内访客更流畅,给本站加了一层 Cloudflare Pages 双部署:GitHub Pages 保持不变(原有链接不断),Cloudflare Pages 作为国内加速入口,两个域名内容同步。

双部署有什么用:

  • 国内加速:Cloudflare 有全球 CDN(含国内优化节点),页面、图片、字体加载明显更快,这是最主要的价值;
  • 双保险:一个平台出问题(GitHub 被墙、Pages 服务异常、DNS 污染),另一个域名随时能顶上,站点不”失联”;
  • 评论/友链不受影响:Giscus 评论挂在 GitHub 仓库上、友链数据在配置里,两个域名共用同一套,换域名不丢数据。

两个网址内容完全一致,按网络环境任选:

  • GitHub Pages:https://lycaonide.github.io
  • Cloudflare Pages:https://my-firefly-blog.pages.dev
为什么选 Cloudflare Pages#
  • 免费额度够用:每月 100 GB 流量,静态博客轻松覆盖;
  • 自带全球 CDN:国内节点比 GitHub Pages 快得多;
  • 支持自定义域名、自动 HTTPS
  • 和 GitHub 无缝衔接:可以直接连仓库,也可以用 API Token 手动部署。
方案对比:控制台连接 Git vs API Token#

一开始尝试的是 Cloudflare 控制台「连接到 Git」(Workers 和 Pages → 创建 → 连接到 Git → 授权 GitHub → 选仓库),理想情况是自动构建、push 即部署。但实测遇到了几个坑:

  • 点「Connect GitHub」后反复跳转到 GitHub 的 App 安装配置页(settings/installations),而不是正常的 OAuth 授权;
  • 仓库权限配好后点 Save,授权回调不自动回 CF,仓库列表一直加载不出来;
  • 折腾半天走不到选仓库那一步。

为什么改用 API Token 方案:

  • 不依赖控制台页面:命令行一条命令完成部署,不碰那个绕圈的 OAuth 流程;
  • 可复现、可写进脚本/CI:命令即文档,以后换机器也能一键部署;
  • 权限可精确控制:token 只给「Cloudflare Pages → Edit」权限,用完随时在控制台撤销,比账号级授权更安全。
1. 创建 API Token#

打开 Cloudflare 控制台https://dash.cloudflare.com)→ 右上角头像 → My ProfileAPI TokensCreate Token → 选 Custom token

  • Token name:随意(比如 blog-deploy);
  • Permissions:AccountCloudflare PagesEdit
  • Account resources:Include → 你的账号;
  • 其他默认,点 Create 后复制 token(只显示一次)。

注意:token 相当于账号钥匙,不要提交到代码仓库;部署完可以随时在控制台撤销轮换。

2. 构建并部署(wrangler)#

本地构建产物在 dist/,用 wrangler 直接推上去(Node 自带 npx,无需全局安装):

Terminal window
# 1. 构建(输出 dist/)
pnpm build
# 2. 配置凭据(PowerShell 用 $env: 前缀)
export CLOUDFLARE_API_TOKEN="你的API_TOKEN" # 上一步创建的
export CLOUDFLARE_ACCOUNT_ID="你的账号ID" # CF 控制台 URL 里 /xxx/ 那段
# 3. 创建 Pages 项目(仅首次)
npx wrangler pages project create my-firefly-blog --production-branch main
# 4. 部署 dist 目录
npx wrangler pages deploy dist --project-name my-firefly-blog --branch main

部署成功输出(真实记录):

✨ Success! Uploaded 244 files (17.28 sec)
🌎 Deploying...
✨ Deployment complete! Take a peek over at https://ca824054.my-firefly-blog.pages.dev

生产域名:https://my-firefly-blog.pages.dev

3. 验证#

浏览器打开 https://my-firefly-blog.pages.dev,内容和 GitHub Pages 完全一致(同一个 dist 构建产物),国内访问明显更流畅:

Cloudflare Pages 部署后的博客首页
Cloudflare Pages 部署后的博客首页

Cloudflare 控制台的部署记录页(Production 域名 + 每次部署的提交信息、状态、预览地址):

Cloudflare Pages 部署记录页
Cloudflare Pages 部署记录页

4. 后续自动化:push 双平台同步#

目前 GitHub Pages 是 push 自动部署,Cloudflare Pages 是手动 wrangler pages deploy。想做到推一次代码、两个平台同时更新,在仓库新建 .github/workflows/deploy-cloudflare.yml,用官方 cloudflare/wrangler-action@v3

name: Deploy to Cloudflare Pages
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.22.0
run_install: false
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Build
run: pnpm run build
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name my-firefly-blog --branch main

使用前提:在 GitHub 仓库 Settings → Secrets and variables → Actions 里添加两个 secret:

  • CLOUDFLARE_API_TOKEN:Cloudflare API Token(创建方法见上文);
  • CLOUDFLARE_ACCOUNT_ID:Cloudflare 账号 ID(控制台 URL 里 /xxx/ 那段)。

配好之后,每次 git push 会自动构建并同时部署到 GitHub Pages 和 Cloudflare Pages。

5. 自定义域名(可选)#

Cloudflare Pages 支持绑定自定义域名(免费,自动 HTTPS),在项目页 → Custom domains 里添加即可。本站暂时用 pages.dev 子域名,等有合适域名再绑。绑定后原 pages.dev 域名依然可用,不影响现有访问。

十一、总结#

这次迁移的核心经验:

  1. 配置收敛:装饰、评论、友链等开关集中在 config 文件里,方便统一管理;
  2. 双站过渡:新旧站并存,内容迁完再下线,风险可控;
  3. 自动化部署:GitHub Actions 让发布变成”push 就完事”。

最终效果就是你现在看到的这个站:Astro 7 + Firefly 主题 + GitHub Pages,樱花、评论、友链齐全,加载快还免费。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

从Hexo迁移到Astro部署记录
https://lycaonide.github.io/posts/从hexo迁移到astro部署记录/
作者
lycaonide
发布于
2026-09-11
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lycaonide
ROS2 / AI / Web 开发,学习中的学生开发者,记录踩坑与成长。
公告
本站已从 Hexo 迁移至 Astro,启用了 Giscus 评论和友链页。2028 考研备考中,更新随缘,欢迎交流。
分类
标签
站点统计
文章
5
分类
1
标签
11
总字数
11,014
运行时长
0
最后活动
0 天前
站点信息
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录