docs: 全量梳理文档结构;补 Gitea 迁移指南 / 架构精简候选;架构总览补证书管家子系统

用户四问:「把项目文件全部理一遍 还有readme文件」「gitea 后面可能还要迁移 帮我写一个迁移文档」
「证书管家做完了吗」「感觉现在架构还是复杂了」

一、文档梳理:根目录 15 个 md → 2 个

根目录只留 README.md(入口)+ 架构总览.md(现状唯一事实源),其余全部归位:

  docs/
  ├── README.md              ← 新增:文档地图(入口 / 当前有效 / 历史归档 三层)
  ├── 证书管家.md             ← 原「函数版证书管家-方案.md」改名
  │                              (它早已是「现状+沿革」文档,标题名不副实 —— 签发早已不在 CF Worker)
  ├── Gitea迁移指南.md        ← 新增
  ├── 架构精简候选.md         ← 新增
  ├── CNB构建落地方案.md / GITEA_SECRETS.md
  └── archive/{平台与选型,功能与修复,主题与内容}/   ← 32 份

- 32 个归档文档统一加 `> 📦 本文档已归档` banner,并指向架构总览
  —— 这个仓库历史文档里全是「权威口径」,混看很容易拿废弃结论当现状
- gitea-backup/ → deploy/gitea/(原名「backup」不准,它是部署包;与其它 deploy 单元并列)
- 新增 md 链接校验(python 脚本,中文路径用 sed 不安全)→ 首轮 18 处断链
  (多一层目录要让 banner 里的相对路径补一个 ../)→ 修正后 0 断链

二、新增 docs/Gitea迁移指南.md(★ 有死线:境外 VPS 11 月到期)

★ 核心价值是「指出迁移面已大幅缩小」:仓库原有的 deploy/gitea/迁移前检查清单.md
(2026-10-04)是为「Gitea + act_runner + 又拍云同步 *整套* 搬迁」写的,**那个前提已经不存在**——
构建归 CNB,runner / upyun-sync / 中转机 / COS 都不需要了。迁移实际只剩「搬数据卷 + 改地址」。

★★ 全文最重要:本仓有 9 处写死了旧地址,按易漏程度排序(1-3 本机,4-5 线上)
  1 本机 .git/config 的 gitea remote
  2 scripts/setup-cnb-remotes.sh 的 GITEA_URL 默认值
  3 deploy/editor-api/bootstrap.sh 的 GITEA_URL 默认值
  4 ★ /srv/editor-api/.env 的 GITEA_URL      ← 漏了会「持续报错但只警告不阻断」,没人发现
  5 ★ /srv/blog 的 gitea remote               ← 同上
  6 docs/GITEA_SECRETS.md
  7-9 架构总览.md / README.md / editor-api/README.md
(不用改:docker-compose.editor.yml、server.mjs、pushall 别名 —— 只引用 remote 名字)

另含:建议新实例改用域名而非 IP(以后搬机器只改 DNS;⚠️ Gitea 28 起只读 ROOT_URL 不读
[server] DOMAIN);rsync 而非 dump(属主必须 uid/gid 1000);切换顺序「先建新的→验证→再拆旧的」;
验证清单强调 `git push gitea --dry-run`;第九节给出「干脆不留这个辅仓」的选项与判断依据。

顺带修掉两个硬伤:
- deploy/gitea/docker-compose.yml 的镜像 tag `1.28.0-rootless` 根本不存在
  (Gitea 28 起去掉 1. 前缀)→ 改 28.0.0-rootless(pin 死,不用 latest)
- deploy/gitea/.gitignore 漏了 backups/(跑一次备份就会把含 secrets 的 dump 写进历史)

三、证书管家核实(结论:已上线运行,2 个遗留)

线上实测:容器 Up (healthy);/preflight 7/7 全过;3 组域名;下次自动续期 04:10。

遗留 ① writeapi.usj.cc 证书没纳管(真问题):nginx 配置在 /www/conf.d/writeapi.usj.cc.conf,
不在 /www/sites/ 管理树里 → 1Panel 的 ssl/upload+sslID 物化碰不到它。
实测仍是 RSA / CN=usj.cc / 到期 2026-12-07,本项目续期不会更新 → 12 月会断。
遗留 ② dnsapi.usj.cc 没上 HTTPS。
(澄清假问题:200181.xyz 公网看到的 LE 证书是 CF 自家边缘证书,与本项目无关)

四、新增 docs/架构精简候选.md(回应「架构还是复杂了」)

结论:复杂度不在件数,在「跨 6 个环境,其中 4 个要自己维护」。国内机 9 个容器里属于本项目的
只有 3 个,能动的只有 2 个。5 个候选 + 建议执行顺序:
  1 停 certimate 容器(工作流已全停用)——先 stop 观察,别急着删
  2 cn-dns-helper 大概率已是遗留(「Worker 侧签发」时代产物;现在签发在国内机且自带 dnsprovider,
    /preflight 显示 CF 凭据可读;Worker 侧 dnsremoted.ts 已无任何引用)
  3 Gitea:迁 or 不留
  4 两套写作前端收敛(本轮不动,先看使用频率)
  5 writeapi.usj.cc 证书纳管(12 月死线)
并明确列出 6 项「必要复杂度,不建议动」。

五、架构总览补齐证书管家子系统(★ 之前完全缺席)

一个完整子系统在「事实源文档」里一个字都没有 —— 这本身就是文档债。补:
- §0 一句话(四→五个子系统)、§1.1 子系统表、§1.2 地址地图两行
- **新增 §5.7 证书管家**:职责划分 / 为什么非要这么分(Worker 免费版 CPU 10ms)/
  三条不能破的红线(Worker 不得签发 · 证书路由只认 Bearer 会话 · 角色必须正着枚举放行)/
  纳管域名表 / 与 certimate 的关系 / 已知遗留
- .cnb.yml 行数 284 → 337(数字漂了);头部更新时间 → 2026-10-06;附录表改指 docs/
- §6 待办:#13(证书遗留)、#14(复杂度盘点)新增;#7 改写为「Gitea 11 月到期,★ 有死线」

六、记忆

.workbuddy/memory/MEMORY.md 新增「文档结构」「Gitea 迁移死线」两节;证书管家节补实测与遗留。
This commit is contained in:
zqlit committed 2026-10-06 22:27:36 +08:00
1 parent d3da972c3b
commit 7abef4ac13
48 files changed
+782 -100

No files matched your search

@@ -0,0 +1,184 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 🧹 Ying主题文档清理指南
## 📋 需要删除的文件(20个)
这些文件都已经整理到 `docs/性能优化文档/` 文件夹,可以安全删除。
### 删除命令(Windows CMD)
```bash
cd E:\GitHub\blog
# 删除优化文档
del themes\Ying\OPTIMIZATION_REPORT.md
del themes\Ying\OPTIMIZATION_STEP1_PLAN.md
del themes\Ying\TEST_STEP1.md
del themes\Ying\OPTIMIZATION_STEP2_PLAN.md
del themes\Ying\OPTIMIZATION_STEP2_FINAL.md
del themes\Ying\OPTIMIZATION_STEP3_PLAN.md
del themes\Ying\OPTIMIZATION_COMPLETE_GUIDE.md
del themes\Ying\IMPLEMENTATION_SUMMARY.md
del themes\Ying\TEST_JS_OPTIMIZATION.md
del themes\Ying\GUIDE_FONT_SUBSETTING.md
del themes\Ying\PLAN1_COMPLETE_SUMMARY.md
del themes\Ying\PJAX_COMPATIBILITY.md
del themes\Ying\PJAX_FIX_SUMMARY.md
del themes\Ying\FONT_OPTIMIZATION_MANUAL.md
del themes\Ying\FONT_OPTIMIZATION_FALLBACK.md
del themes\Ying\FINAL_FONT_TEST.md
del themes\Ying\GITHUB_ACTIONS_GUIDE.md
del themes\Ying\COMMIT_GUIDE.md
del themes\Ying\ACTIONS_FIX_GUIDE.md
del themes\Ying\PROJECT_COMPLETE_SUMMARY.md
```
### 删除命令(Mac/Linux)
```bash
cd E:\GitHub\blog
# 删除优化文档
rm themes/Ying/OPTIMIZATION_REPORT.md
rm themes/Ying/OPTIMIZATION_STEP1_PLAN.md
rm themes/Ying/TEST_STEP1.md
rm themes/Ying/OPTIMIZATION_STEP2_PLAN.md
rm themes/Ying/OPTIMIZATION_STEP2_FINAL.md
rm themes/Ying/OPTIMIZATION_STEP3_PLAN.md
rm themes/Ying/OPTIMIZATION_COMPLETE_GUIDE.md
rm themes/Ying/IMPLEMENTATION_SUMMARY.md
rm themes/Ying/TEST_JS_OPTIMIZATION.md
rm themes/Ying/GUIDE_FONT_SUBSETTING.md
rm themes/Ying/PLAN1_COMPLETE_SUMMARY.md
rm themes/Ying/PJAX_COMPATIBILITY.md
rm themes/Ying/PJAX_FIX_SUMMARY.md
rm themes/Ying/FONT_OPTIMIZATION_MANUAL.md
rm themes/Ying/FONT_OPTIMIZATION_FALLBACK.md
rm themes/Ying/FINAL_FONT_TEST.md
rm themes/Ying/GITHUB_ACTIONS_GUIDE.md
rm themes/Ying/COMMIT_GUIDE.md
rm themes/Ying/ACTIONS_FIX_GUIDE.md
rm themes/Ying/PROJECT_COMPLETE_SUMMARY.md
```
---
## ✅ 保留的文件
**不要删除这些文件:**
- ✅ `README.md` - 主题说明文档
- ✅ `archetypes/post.md` - Hugo模板文件
---
## 📊 清理验证
### 清理前
```bash
dir themes\Ying\*.md
```
**应该看到20个优化文档**
### 清理后
```bash
dir themes\Ying\*.md
```
**应该只看到2个文件:**
- README.md
- archetypes\post.md
---
## 💡 已整理的文档位置
**所有优化文档已整理到:**
```
E:\GitHub\blog\docs\性能优化文档\
```
**索引文档:**
```
E:\GitHub\blog\docs\性能优化文档\README.md
```
---
## 🎯 快速清理命令
### 一键清理(Windows PowerShell)
```powershell
cd E:\GitHub\blog
# 删除所有优化文档
Remove-Item themes\Ying\OPTIMIZATION_*.md -Force
Remove-Item themes\Ying\TEST_*.md -Force
Remove-Item themes\Ying\PLAN1_*.md -Force
Remove-Item themes\Ying\PJAX_*.md -Force
Remove-Item themes\Ying\FONT_*.md -Force
Remove-Item themes\Ying\FINAL_*.md -Force
Remove-Item themes\Ying\GITHUB_*.md -Force
Remove-Item themes\Ying\COMMIT_*.md -Force
Remove-Item themes\Ying\ACTIONS_*.md -Force
Remove-Item themes\Ying\PROJECT_*.md -Force
Remove-Item themes\Ying\IMPLEMENTATION_*.md -Force
Remove-Item themes\Ying\GUIDE_*.md -Force
Remove-Item themes\Ying\OPTIMIZATION_COMPLETE_GUIDE.md -Force
Write-Host "✅ 清理完成"
```
---
## 📝 清理后提交
```bash
# 查看删除的文件
git status
# 提交清理
git add -A
git commit -m "docs: 清理Ying主题冗余文档
- 删除20个优化相关文档
- 已整理到 docs/性能优化文档/ 文件夹
- 保留 README.md 和 archetypes/post.md"
# 推送
git push origin main
```
---
## 🎉 清理完成
### 清理效果
- ✅ Ying主题目录更简洁
- ✅ 只保留必要文件
- ✅ 优化文档已整理到专门文件夹
- ✅ 便于维护和查找
### 文档位置
**所有优化文档:**
```
E:\GitHub\blog\docs\性能优化文档\
```
**索引文档:**
```
E:\GitHub\blog\docs\性能优化文档\README.md
```
---
**清理指南完成!** 🎉
**按照指南操作即可清理Ying主题中的冗余文档!**
@@ -0,0 +1,357 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 博客字体优化方案 - 文章规划
## 📋 文章定位
- **目标读者:** 生活博主、非技术背景博主
- **文章风格:** 通俗易懂、重效果、少术语
- **核心卖点:** 用真实案例告诉你,字体优化能让博客快多少
---
## 📝 推荐文章结构
### 标题建议(选一个)
1. **我的博客加载快了50%,只因为改了字体**
2. **博客太慢?可能是字体拖后腿了**
3. **从3秒到1秒:我是怎么优化博客字体的**
4. **博客字体优化:一个让网站变快的简单技巧**
**推荐:** 第1个(有具体数字,有悬念)
---
### 文章大纲
```
一、开头:我的博客有多慢?(引入问题)
二、发现问题:原来是字体拖后腿(诊断过程)
三、字体优化到底是什么?(通俗解释)
四、我是怎么做的(真实案例)
五、效果对比(数据说话)
六、你也可以这样做(通用方案)
七、总结
```
---
## 📝 详细内容规划
### 一、开头:我的博客有多慢?(200字)
**写法:** 从用户视角出发,描述痛点
**要点:**
- 博客打开要3秒以上
- 移动端更慢,用户等不及就走了
- 用了各种缓存、CDN都没用
- 最后发现是字体文件太大
**示例段落:**
> 我的博客一直有个问题:打开太慢。
>
> 用PageSpeed Insights测了一下,移动端得分只有60分。加载时间3秒多,这在现在这个"3秒定生死"的时代,基本等于告诉用户"别等了,走吧"。
>
> 我试过开CDN、压缩图片、用缓存插件,效果都不明显。直到有一天,我在Network面板里发现了一个1.2MB的字体文件...
---
### 二、发现问题:原来是字体拖后腿(300字)
**写法:** 用通俗语言解释问题
**要点:**
- 中文字体为什么这么大?(2万个字符)
- 你的博客实际用了多少字?(可能只有2000个)
- 浪费了90%的带宽在不需要的字上
**类比:**
> 这就好比你要寄一封信,但邮局让你把整本字典一起寄出去。你只用了里面的几十个字,却要为整本字典的重量买单。
**数据展示:**
```
原始字体:1,226 KB(包含20,000+字符)
实际使用:2,485字符
浪费:90%的字符从未出现过
```
---
### 三、字体优化到底是什么?(200字)
**写法:** 用比喻解释,避免术语
**核心概念:**
- 字体子集化:只保留你用到的字
- 就像"按需定制",不是"全买"
**比喻:**
> 字体优化就像是去餐厅点菜。
>
> 原来的方式:把整本菜单都买下来,只吃其中几道。
> 优化后的方式:只点你要吃的菜,吃完走人。
>
> 结果:花更少的钱,吃得一样饱。
**技术原理(简化版):**
1. 扫描你博客的所有文章
2. 提取实际使用的字符
3. 生成一个"精简版"字体
4. 体积减少80%+
---
### 四、我是怎么做的(真实案例)(500字)
**写法:** 分步骤讲解,附代码和截图
**步骤1:分析现状**
```
工具:Chrome DevTools → Network面板
发现:zql-v2.woff2 文件大小 1.2MB
```
**步骤2:选择工具**
- 推荐:Python + fonttools
- 备选:在线工具(无需编程)
**步骤3:提取字符**
```bash
# 运行脚本,自动提取
python subset-font.py
```
**步骤4:生成子集**
```
结果:
- 原始:1,226 KB → 子集:740 KB
- 减少:40%
```
**步骤5:更新CSS**
```css
/* 修改前 */
src: url('../font/zql-v2.woff2');
/* 修改后 */
src: url('../font/zql-v2-subset.woff2');
```
**截图建议:**
1. Network面板显示字体大小对比
2. 脚本运行结果截图
3. 优化前后文件大小对比
---
### 五、效果对比(数据说话)(300字)
**写法:** 用图表和数据展示效果
**数据表格:**
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| 字体大小 | 1.2 MB | 740 KB | -40% |
| 加载时间 | 3.2秒 | 1.8秒 | -44% |
| PageSpeed得分 | 60 | 85 | +42% |
| 移动端体验 | 差 | 良好 | ✅ |
**用户体感:**
> 优化后最明显的变化是:页面"刷"一下就出来了。
>
> 以前打开文章,能明显看到文字先显示为方块,然后才变成正常字体。现在几乎看不到这个过程了。
**截图建议:**
1. PageSpeed Insights优化前后对比
2. Network面板加载瀑布图
3. 实际打开速度对比GIF
---
### 六、你也可以这样做(通用方案)(400字)
**写法:** 提供可复用的方案
**方案A:在线工具(零代码)**
1. 访问 fontsquirrel.com/webfont-generator
2. 上传你的字体文件
3. 选择"Expert"模式
4. 勾选"Custom Subsetting"
5. 下载优化后的字体
6. 替换原文件,更新CSS
**方案B:Python脚本(推荐)**
```bash
# 安装工具
pip install fonttools brotli
# 运行脚本
python subset-font.py
```
**方案C:找我帮忙(最简单)**
- 提供你的博客地址
- 我帮你分析和优化
- 全程指导,保证效果
**注意事项:**
- 优化前备份原文件
- 测试所有页面的显示效果
- 特殊符号可能需要单独处理
---
### 七、总结(150字)
**要点回顾:**
1. 中文字体很大,但你用的字很少
2. 字体优化可以减少40-90%的大小
3. 加载速度提升明显
4. 操作并不复杂
**结尾金句:**
> 博客优化就像减肥:找对方法,效果立竿见影。
>
> 字体优化就是那个你一直忽略、但效果惊人的"减肥方法"。
>
> 试试看,你的博客也能快50%。
---
## 📊 文章数据
### 预期指标
- **字数:** 2000-2500字
- **阅读时间:** 8-10分钟
- **代码块:** 5-6个(简化版)
- **截图:** 8-10张
- **表格:** 2-3个
### SEO关键词
- 博客优化
- 字体优化
- 网站加速
- Hugo优化
- 字体子集化
- PageSpeed提升
---
## 🎨 排版建议
### 标题层级
- H2:大章节(7个)
- H3:小节(每章节2-3个)
- H4:更细的点(少用)
### 视觉元素
- ✅ 数据表格(对比效果)
- ✅ 代码块(简化版,带注释)
- ✅ 截图(Network面板、PageSpeed)
- ✅ 比喻图(餐厅点菜的比喻)
- ✅ 对比图(优化前后)
### 代码展示原则
- 只展示必要的代码
- 每行代码都有注释
- 提供"复制即用"的版本
- 避免大段代码块
---
## 📸 截图清单
### 必须截图
1. [ ] PageSpeed Insights优化前得分
2. [ ] PageSpeed Insights优化后得分
3. [ ] Network面板字体文件大小
4. [ ] 脚本运行结果
5. [ ] 优化前后文件大小对比
### 建议截图
6. [ ] 字体加载瀑布图
7. [ ] 实际打开速度对比
8. [ ] 移动端测试结果
---
## ✍️ 写作技巧
### 开头吸引人
- 用具体数字("3秒"、"50%")
- 描述用户痛点("用户等不及就走了")
- 制造悬念("最后发现是...")
### 中间讲清楚
- 用比喻解释技术概念
- 分步骤讲解,每步都清晰
- 提供"复制即用"的代码
### 结尾有力量
- 总结核心要点
- 给出行动建议
- 用金句收尾
### 语言风格
- ❌ 避免:"首先、其次、最后"
- ❌ 避免:"众所周知"
- ✅ 使用:"我发现"、"我试过"
- ✅ 使用:"你也可以"、"很简单"
---
## 🎯 文章亮点
### 独特卖点
1. **真实数据** - 不是理论,是实际优化结果
2. **通俗易懂** - 生活博主也能看懂
3. **可复用** - 读者可以直接照着做
4. **效果明显** - 40%的提升很震撼
### 差异化
- 其他文章:讲技术原理
- 你的文章:讲实际效果 + 简单方案
---
## 📅 写作计划
### 第1天:素材收集
- [ ] 整理优化前后的数据
- [ ] 截图关键页面
- [ ] 准备代码示例
### 第2天:初稿写作
- [ ] 按大纲写完整初稿
- [ ] 插入截图和代码
- [ ] 检查逻辑连贯性
### 第3天:润色发布
- [ ] 精简语言,删除冗余
- [ ] 优化标题和开头
- [ ] 检查错别字
- [ ] 发布并分享
---
## 💡 进阶建议
### 如果想更深入
- 添加"常见问题"章节
- 提供不同Hugo主题的适配方案
- 分享自动化脚本
### 如果想更简洁
- 只保留"问题→方案→效果"三部分
- 删除技术细节,只留结论
- 用对比图代替文字描述
---
**文档版本:** v1.0
**创建日期:** 2026-06-03
**适用场景:** 博客字体优化技术文章
@@ -0,0 +1,166 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 📜 添加古风官职字符到字体子集
## 📋 需要添加的字符
古风官职文本中的特殊字符(可能缺失):
```
— 庶 民 太 尉 谏 给 簿 司 户 录 丞 判 殿
```
---
## 🚀 方法1:运行Python脚本(推荐)
```bash
cd E:\GitHub\blog
# 运行合并脚本
python scripts/merge_ancient_chars.py
# 重新生成字体子集
python scripts/subset-font-safe.py
```
---
## 🚀 方法2:手动添加字符
### 步骤1:编辑字符列表
打开文件:`themes/Ying/static/font/used_chars.txt`
在文件末尾添加:
```
—庶民太尉谏给簿司户录丞判殿
```
### 步骤2:重新生成字体子集
```bash
cd E:\GitHub\blog
# 重新生成字体子集
python scripts/subset-font-safe.py
```
---
## 🧪 验证添加成功
### 检查字符列表
```bash
# 查看字符列表
type themes\Ying\static\font\used_chars.txt
# 搜索特殊字符
findstr "—" themes\Ying\static\font\used_chars.txt
```
### 检查字体文件
```bash
# 查看字体文件大小
dir themes\Ying\static\font\zql-v2-subset.*
```
**预期:** 字体文件大小应该略有增加(包含新字符)
---
## 📊 预期效果
### 字体大小变化
| 指标 | 添加前 | 添加后 | 变化 |
|------|--------|--------|------|
| **字符数** | 2,485 | ~2,500 | +15 |
| **字体大小** | 757KB | ~760KB | +3KB |
**说明:** 增加15个字符,字体大小增加约3KB(可忽略)
---
## 🎯 使用场景
### 古风官职系统
这些字符用于:
```
正一品 太师 2000
从一品 太尉 1400
正二品 参知政事 1050
...
正九品 主簿 15
从九品 司户参军 5
— 庶民 0
```
**应用场景:**
- 古风游戏
- 历史题材文章
- 等级系统展示
---
## 💡 后续优化
### 如果还有其他特殊字符
1. 收集所有需要的特殊字符
2. 添加到 `used_chars.txt`
3. 重新运行字体子集化
### 自动化
**GitHub Actions会自动处理:**
- 当你发布包含这些字符的文章时
- 自动重新生成字体子集
- 自动部署到UpYun
---
## 🔄 回滚方案
### 如果出现问题
```bash
# 恢复原始字符列表
git checkout themes/Ying/static/font/used_chars.txt
# 重新生成字体子集
python scripts/subset-font-safe.py
```
---
## ✅ 完成确认
### 检查清单
- [ ] 字符已添加到 used_chars.txt
- [ ] 字体子集已重新生成
- [ ] 字体文件大小略有增加
- [ ] 新字符可以正常显示
### 测试
```bash
# 启动Hugo
hugo server -D
# 访问包含古风官职的页面
# 检查字符是否正常显示
```
---
**添加古风官职字符指南完成!** 🎉
**按照指南操作即可完成字符添加!**
@@ -0,0 +1,302 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 01-项目完成总结
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已完成
---
## 🎉 项目概述
### 项目目标
对Hugo主题Ying进行全面性能优化,包括:
- JS按需加载优化
- 字体子集化优化
- GitHub Actions自动化
### 优化效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **总体资源** | ~3MB | ~1.5MB | ⚡ -50% |
| **加载速度** | 慢 | 快 | ⚡ +40% |
| **Lighthouse** | 60 | 75-80 | ⚡ +33% |
---
## ✅ 已完成的工作
### 1. JS按需加载优化(第1步)
**完成时间:** 2026-06-03
**优化效果:**
- 首页JS:800KB → 350KB(⚡ -56%)
- 文章页JS:800KB → 450KB(⚡ -44%)
**主要工作:**
- ✅ JS代码拆分为4个bundle
- ✅ 核心JS始终加载
- ✅ 页面特定JS按需加载
- ✅ 非关键JS延迟加载
- ✅ PJAX完美适配
**修改文件:**
- `themes/Ying/layouts/partials/footer.html`
- `themes/Ying/assets/js/modules/mypjax.js`
---
### 2. 字体子集化优化(第2步)
**完成时间:** 2026-06-03
**优化效果:**
- 字体大小:1.2MB → 757KB(⚡ -37%)
**主要工作:**
- ✅ 使用Python fonttools提取字符
- ✅ 生成子集字体(2,485个字符)
- ✅ 更新CSS字体声明
- ✅ 保持所有字符正常显示
**修改文件:**
- `themes/Ying/assets/css/main.css`
- `themes/Ying/static/font/zql-v2-subset.woff2`
- `themes/Ying/static/font/zql-v2-subset.woff`
- `themes/Ying/static/font/used_chars.txt`
---
### 3. GitHub Actions自动化(第3步)
**完成时间:** 2026-06-03
**自动化程度:** 100%
**主要工作:**
- ✅ 创建字体子集化工作流
- ✅ 配置自动触发条件
- ✅ 与deploy.yml完美协调
- ✅ 智能检测变更
- ✅ 自动部署到UpYun
**创建文件:**
- `.github/workflows/subset-fonts.yml`
- `requirements.txt`
---
## 📊 技术实现
### JS优化策略
```
core.js (200KB) - 始终加载
├── UIkit
├── 图标字体
├── 图片灯箱
├── 工具函数
├── 搜索功能
├── 浮动工具
├── 进度条
├── PJAX
└── 主题主逻辑
page-only.js (180KB) - 文章详情页
├── Artalk评论
├── 段落评论
└── 打赏功能
deferred.js (25KB) - 延迟加载
├── Toast消息
└── 图片懒加载
infinite-scroll.js (20KB) - 首页(如果启用)
tiaozhuan.js (8KB) - 特定页面
```
### 字体优化策略
```
原始字体(1.2MB)
├── 20,000+ 字符
└── 完整中文字符集
子集字体(757KB)
├── 2,485 个字符
├── 常用中文字符
├── 英文字母和数字
├── 常用标点符号
└── 特殊符号
优化效果:-37%
```
### 自动化流程
```
用户push内容更新
↓
deploy.yml(部署文章)
↓
subset-fonts.yml(优化字体)
↓
deploy.yml(部署新字体)
↓
✅ 完成!
```
---
## 🎯 项目亮点
### 1. 性能显著提升
- ✅ 资源减少50%
- ✅ 加载速度提升40%
- ✅ Lighthouse 75-80分
- ✅ 用户体验大幅改善
### 2. 完全自动化
- ✅ GitHub Actions自动运行
- ✅ 智能检测变更
- ✅ 无需手动干预
- ✅ 节省时间和精力
### 3. 智能优化
- ✅ 只在需要时优化
- ✅ 避免不必要的部署
- ✅ 节省资源和成本
- ✅ 保持系统高效
### 4. 完整文档
- ✅ 16份详细文档
- ✅ 覆盖所有场景
- ✅ 故障排除指南
- ✅ 最佳实践说明
---
## 📁 文档清单
### 核心文档(3份)
- ✅ 01-项目完成总结.md(本文件)
- ✅ 02-方案1完成总结.md
- ✅ 03-三步优化完整指南.md
### 优化实施(4份)
- ✅ 04-JS按需加载优化.md
- ✅ 05-PJAX适配说明.md
- ✅ 06-PJAX修复总结.md
- ✅ 07-字体子集化优化.md
### 自动化(3份)
- ✅ 08-GitHub-Actions使用指南.md
- ✅ 09-Actions修复指南.md
- ✅ 10-提交指南.md
### 测试验证(2份)
- ✅ 11-JS优化测试指南.md
- ✅ 12-字体优化测试指南.md
### 详细方案(4份)
- ✅ 13-主题全面优化分析.md
- ✅ 14-JS优化最终方案.md
- ✅ 15-字体优化手动指南.md
- ✅ 16-实施总结报告.md
---
## 💡 后续使用
### 日常开发
```bash
# 发布新文章
git add content/posts/new-article.md
git commit -m "feat: new article"
git push origin main
# 等待自动化(3-10分钟)
# - deploy.yml:部署文章
# - subset-fonts.yml:优化字体(如果需要)
# - deploy.yml:部署新字体(如果需要)
```
### 监控系统
```bash
# 查看GitHub Actions
https://github.com/zqlit/blog/actions
# 查看工作流状态
- Deploy to Production(部署)
- Font Subset Optimization(字体优化)
```
### 性能测试
```bash
# 每月测试一次
# 使用Chrome DevTools的Lighthouse
# 或者:https://pagespeed.web.dev/
# 记录:Performance、FCP、LCP、TTI
```
---
## 🎓 学到了什么?
### 技术技能
- ✅ JavaScript代码拆分
- ✅ 字体子集化技术
- ✅ GitHub Actions工作流
- ✅ Hugo静态站点优化
### DevOps实践
- ✅ CI/CD流程设计
- ✅ 自动化部署
- ✅ 工作流协调
- ✅ 性能监控
---
## 🏆 成就解锁
- ⚡ **性能优化大师** - 资源减少50%
- 🤖 **自动化专家** - 完整CI/CD流程
- 🚀 **前端优化师** - Lighthouse 75-80分
- 💡 **DevOps工程师** - GitHub Actions精通
---
## 🎊 项目完成
**恭喜你完成了完整的Hugo博客性能优化和自动化系统!**
- ✅ 性能提升50%
- ✅ 加载速度提升40%
- ✅ Lighthouse 75-80分
- ✅ 完全自动化
- ✅ 生产就绪
**现在可以专注于创作优质内容了!** 🚀
---
**项目完成时间:** 2026-06-03
**总耗时:** 约6小时
**优化效果:** 性能提升50%
**自动化程度:** 100%
**维护成本:** 0(完全自动化)
**祝你博客越办越好!** 🎉
@@ -0,0 +1,364 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 02-方案1完成总结
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已完成
**方案:** 保守优化(JS + 字体,不改变CSS加载方式)
---
## 📋 方案概述
### 为什么选择方案1(保守优化)?
**原因:**
1. ✅ 零风险,不会破坏现有功能
2. ✅ 快速实施(3-4小时)
3. ✅ 仍然获得显著性能提升
4. ✅ 样式完全不变
**对比方案2(激进优化):**
- 方案2包括CSS内联优化
- 需要100%提取所有CSS,风险高
- 可能遗漏某些样式
- 维护成本高
---
## ✅ 已完成的优化
### 第1步:JS按需加载优化
**完成时间:** 2026-06-03
**优化效果:**
- 首页JS:800KB → 350KB(⚡ -56%)
- 文章页JS:800KB → 450KB(⚡ -44%)
**主要工作:**
- ✅ JS代码拆分为4个bundle
- ✅ 核心JS始终加载(~200KB)
- ✅ 页面特定JS按需加载(~180KB)
- ✅ 非关键JS延迟加载(~25KB)
- ✅ PJAX完美适配
**修改文件:**
- `themes/Ying/layouts/partials/footer.html`
- `themes/Ying/assets/js/modules/mypjax.js`
---
### 第2步:字体子集化优化
**完成时间:** 2026-06-03
**优化效果:**
- 字体大小:1.2MB → 757KB(⚡ -37%)
**主要工作:**
- ✅ 使用Python fonttools提取字符
- ✅ 生成子集字体(2,485个字符)
- ✅ 更新CSS字体声明
- ✅ 保持所有字符正常显示
**修改文件:**
- `themes/Ying/assets/css/main.css`
- `themes/Ying/static/font/zql-v2-subset.woff2`
- `themes/Ying/static/font/zql-v2-subset.woff`
- `themes/Ying/static/font/used_chars.txt`
---
## 📊 优化效果
### 性能提升数据
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **JS(首页)** | 800KB | 350KB | ⚡ -56% |
| **JS(文章页)** | 800KB | 450KB | ⚡ -44% |
| **字体** | 1.2MB | 757KB | ⚡ -37% |
| **总体资源(首页)** | ~3MB | ~1.5MB | ⚡ -50% |
| **总体资源(文章页)** | ~3MB | ~1.6MB | ⚡ -47% |
| **FCP** | 2.5s | ~1.5s | ⚡ -40% |
| **TTI** | 4.0s | ~2.0s | ⚡ -50% |
| **TBT** | 350ms | ~100ms | ⚡ -71% |
| **Lighthouse** | 60 | 75-80 | ⚡ +25-33% |
---
## 🔧 技术实现
### JS优化策略
**核心JS(始终加载):**
- UIkit框架
- 图标字体
- 图片灯箱
- 工具函数
- 搜索功能
- 浮动工具
- 进度条
- PJAX
- 主题主逻辑
**页面特定JS(按需加载):**
- Artalk评论系统(文章详情页)
- 段落评论(文章详情页)
- 打赏功能(文章详情页)
**延迟加载的JS:**
- Toast消息
- 图片懒加载
---
### 字体优化策略
**方法:** Python fonttools
**步骤:**
1. 构建Hugo站点
2. 扫描所有HTML和CSS文件
3. 提取实际使用的字符
4. 生成子集字体
5. 更新CSS字体声明
**结果:**
- 提取了2,485个字符
- 字体大小减少37%
- 保持所有字符正常显示
---
## ✅ 测试验证
### 功能测试
- ✅ 首页功能正常
- ✅ 文章详情页正常
- ✅ 评论区正常加载
- ✅ 打赏功能正常
- ✅ 深色模式正常
- ✅ 响应式布局正常
### 性能测试
- ✅ Network面板显示JS大小减少
- ✅ Network面板显示字体大小减少
- ✅ Lighthouse得分提升
- ✅ 无Console错误
---
## 💡 优势和劣势
### 优势 ✅
1. **零风险** - 不会破坏现有功能
2. **快速实施** - 3-4小时完成
3. **显著提升** - 性能提升50%
4. **样式不变** - CSS保持不变
5. **易于维护** - 代码结构清晰
### 劣势 ⚠️
1. **CSS未优化** - 仍有优化空间
2. **字体优化有限** - 只减少37%(预期87%)
3. **需要Python** - 字体优化依赖Python环境
---
## 🔄 与方案2对比
### 方案1(保守优化)✅ 已选择
**优化内容:**
- JS按需加载
- 字体子集化
- 保持CSS不变
**预期效果:**
- 性能提升40-50%
- Lighthouse 75-80分
**风险:** 低
**实施时间:** 3-4小时
---
### 方案2(激进优化)❌ 未选择
**优化内容:**
- CSS内联优化
- JS按需加载
- 字体子集化
**预期效果:**
- 性能提升60-70%
- Lighthouse 90+分
**风险:** 高
**实施时间:** 8-10小时
---
## 🎯 为什么方案1更好?
### 对于你的场景
1. **零风险** - 生产环境最重要
2. **快速见效** - 立即享受性能提升
3. **保持稳定** - 所有功能正常
4. **易于维护** - 代码结构清晰
5. **成本低** - 无需大量测试
### 如果选择方案2
**可能出现的问题:**
- CSS内联不完整,导致样式丢失
- 需要大量测试验证
- 维护成本高
- 风险大
---
## 📁 修改的文件清单
### 1. JS优化
**修改文件:**
- `themes/Ying/layouts/partials/footer.html`(JS拆分)
- `themes/Ying/assets/js/modules/mypjax.js`(PJAX适配)
**创建文件:**
- 无(使用Hugo资源管道)
---
### 2. 字体优化
**修改文件:**
- `themes/Ying/assets/css/main.css`(字体声明)
**创建文件:**
- `themes/Ying/static/font/zql-v2-subset.woff2`(子集字体)
- `themes/Ying/static/font/zql-v2-subset.woff`(子集字体)
- `themes/Ying/static/font/used_chars.txt`(字符列表)
---
## 🧪 测试清单
### JS优化测试
- [ ] 首页正常显示
- [ ] 导航菜单正常
- [ ] 搜索功能正常
- [ ] 主题切换正常
- [ ] 文章详情页正常
- [ ] 评论区正常加载
- [ ] 打赏功能正常
- [ ] PJAX导航正常
- [ ] 无限滚动正常(如果启用)
### 字体优化测试
- [ ] 中文字符正常
- [ ] 英文字符正常
- [ ] 数字正常
- [ ] 标点符号正常
- [ ] 深色模式正常
- [ ] 移动端正常
### 性能测试
- [ ] Network面板显示JS大小减少
- [ ] Network面板显示字体大小减少
- [ ] Lighthouse得分提升
- [ ] 无Console错误
---
## 💡 使用建议
### 日常开发
```bash
# 发布新文章
git add content/posts/new-article.md
git commit -m "feat: new article"
git push origin main
# 等待自动化(3-10分钟)
# - deploy.yml:部署文章
# - subset-fonts.yml:优化字体(如果需要)
# - deploy.yml:部署新字体(如果需要)
```
### 监控系统
```bash
# 查看GitHub Actions
https://github.com/zqlit/blog/actions
# 查看工作流状态
- Deploy to Production(部署)
- Font Subset Optimization(字体优化)
```
---
## 🔄 后续优化
### 如果需要进一步优化
**选项1:实施CSS内联(方案2的一部分)**
- 风险:高
- 收益:额外提升20-30%
- 建议:谨慎考虑
**选项2:优化字体子集化**
- 使用更大的字符集
- 或者使用系统字体
- 建议:当前方案已足够
**选项3:其他优化**
- 图片优化
- CDN配置
- 缓存策略
- 建议:按需实施
---
## 🎉 项目完成
### 完成情况
- ✅ JS按需加载优化(-56%首页,-44%文章页)
- ✅ 字体子集化优化(-37%)
- ✅ 总体性能提升50%
- ✅ Lighthouse 75-80分
- ✅ 完全自动化
### 下一步
**什么都不用做!** 🚀
- ✅ 系统已经自动化运行
- ✅ 发布新文章时自动优化
- ✅ 享受性能提升
- ✅ 专注于内容创作
---
**方案1完成时间:** 2026-06-03
**总耗时:** 约4小时
**优化效果:** 性能提升50%
**风险等级:** 低(零风险)
**维护成本:** 0(完全自动化)
**方案1是最优选择!** 🎉
@@ -0,0 +1,602 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 03-三步优化完整指南
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已完成
**适用对象:** Hugo主题Ying性能优化
---
## 📋 概述
### 三步优化内容
1. **JS按需加载优化** - 减少56%首页JS
2. **字体子集化优化** - 减少37%字体大小
3. **GitHub Actions自动化** - 100%自动化
### 预期效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **总体资源** | ~3MB | ~1.5MB | ⚡ -50% |
| **加载速度** | 慢 | 快 | ⚡ +40% |
| **Lighthouse** | 60 | 75-80 | ⚡ +33% |
---
## 🚀 第1步:JS按需加载优化
### 1.1 优化目标
将JS拆分为多个bundle,实现按需加载:
- 核心JS始终加载
- 页面特定JS按需加载
- 非关键JS延迟加载
### 1.2 实施步骤
#### 步骤1:分析JS结构
**核心JS(必须加载):**
- UIkit框架
- 图标字体
- 图片灯箱
- 工具函数
- 搜索功能
- 浮动工具
- 进度条
- PJAX
- 主题主逻辑
**页面特定JS(按需加载):**
- Artalk评论系统(文章详情页)
- 段落评论(文章详情页)
- 打赏功能(文章详情页)
**延迟加载的JS:**
- Toast消息
- 图片懒加载
---
#### 步骤2:修改footer.html
**文件:** `themes/Ying/layouts/partials/footer.html`
**修改内容:**
1. 创建核心JS bundle
```gohtml
{{ $coreScripts := slice $iconfont $uikit $viewimage $utils $loader $cache $search $floatingTools $nprogress $pjaxLib $mypjax $pangu $linkify $main | resources.Concat "js/core.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $coreScripts.RelPermalink }}"></script>
```
2. 创建页面特定JS bundle
```gohtml
{{ if .IsPage }}
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
{{ $paragraphComments := resources.Get "js/modules/paragraph-comments.js" }}
{{ $reward := resources.Get "js/modules/reward.js" }}
{{ $pageScripts := slice $artalkModule $paragraphComments $reward | resources.Concat "js/page-only.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $pageScripts.RelPermalink }}"></script>
<script>window._pageOnlyScriptUrl = '{{ $pageScripts.RelPermalink }}';</script>
{{ end }}
```
3. 创建延迟加载JS bundle
```gohtml
{{ $toast := resources.Get "js/modules/toast.js" }}
{{ $easylazyload := resources.Get "js/modules/lazyload.js" }}
{{ $deferredScripts := slice $toast $easylazyload | resources.Concat "js/deferred.js" | resources.Minify | resources.Fingerprint }}
<script>
if ('requestIdleCallback' in window) {
requestIdleCallback(function() {
var script = document.createElement('script');
script.src = '{{ $deferredScripts.RelPermalink }}';
script.defer = true;
document.body.appendChild(script);
});
} else {
setTimeout(function() {
var script = document.createElement('script');
script.src = '{{ $deferredScripts.RelPermalink }}';
script.defer = true;
document.body.appendChild(script);
}, 1000);
}
</script>
```
---
#### 步骤3:适配PJAX
**文件:** `themes/Ying/assets/js/modules/mypjax.js`
**修改内容:**
在 `pjax:complete` 事件中添加动态加载逻辑:
```javascript
// 动态加载页面特定JS(PJAX适配)
var isArticlePage = document.querySelector('#Comments') !== null ||
document.querySelector('.post-content') !== null;
if (isArticlePage && !window._pageOnlyLoaded && window._pageOnlyScriptUrl) {
var script = document.createElement('script');
script.src = window._pageOnlyScriptUrl;
script.onload = function() {
window._pageOnlyLoaded = true;
console.log('page-only.js loaded for PJAX navigation');
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) {}
}
};
document.body.appendChild(script);
}
```
---
### 1.3 预期效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **首页JS** | 800KB | 350KB | ⚡ -56% |
| **文章页JS** | 800KB | 450KB | ⚡ -44% |
| **TTI** | 4.0s | ~2.0s | ⚡ -50% |
| **TBT** | 350ms | ~100ms | ⚡ -71% |
---
### 1.4 测试验证
**功能测试:**
- [ ] 首页功能正常
- [ ] 文章详情页正常
- [ ] 评论区正常加载
- [ ] PJAX导航正常
- [ ] 打赏功能正常
**性能测试:**
- [ ] Network面板显示JS大小减少
- [ ] Lighthouse得分提升
- [ ] 无Console错误
---
## 🚀 第2步:字体子集化优化
### 2.1 优化目标
将中文字体从1.2MB优化到757KB,减少37%。
### 2.2 实施步骤
#### 步骤1:备份原始字体
```bash
cd E:\GitHub\blog
# Windows
copy themes\Ying\static\font\zql-v2.woff2 themes\Ying\static\font\zql-v2.woff2.backup
copy themes\Ying\static\font\zql-v2.woff themes\Ying\static\font\zql-v2.woff.backup
# Mac/Linux
cp themes/Ying/static/font/zql-v2.woff2 themes/Ying/static/font/zql-v2.woff2.backup
cp themes/Ying/static/font/zql-v2.woff themes/Ying/static/font/zql-v2.woff.backup
```
---
#### 步骤2:安装Python依赖
```bash
pip install fonttools brotli
```
---
#### 步骤3:构建Hugo站点
```bash
hugo --destination=public
```
---
#### 步骤4:运行字体子集化
```bash
python scripts/subset-font-safe.py
```
**预期输出:**
```
🔤 字体子集化工具(安全版本)
==================================================
✅ 找到public目录,将扫描构建后的HTML
🔍 扫描目录: content, layouts, public
📝 提取了 2492 个唯一字符
💾 字符列表已保存到: themes/Ying/static/font\used_chars.txt
✂️ 正在生成子集字体...
✅ 子集化完成!
📊 优化结果:
子集字符数: 2485
子集文件大小: 739.7 KB
减少: 486.8 KB (39.7%)
🎉 所有子集字体生成成功!
```
---
#### 步骤5:更新CSS字体声明
**文件:** `themes/Ying/assets/css/main.css`
**修改字体声明:**
```css
@font-face {
font-family: 'zql';
src: url('../font/zql-v2-subset.woff2') format('woff2'),
url('../font/zql-v2-subset.woff') format('woff');
font-display: swap;
}
```
**关键改动:**
- `zql-v2.woff2` → `zql-v2-subset.woff2`
- `zql-v2.woff` → `zql-v2-subset.woff`
- 删除 `unicode-range`
---
### 2.3 预期效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **字体大小** | 1.2MB | 757KB | ⚡ -37% |
| **字符数** | 20,000+ | 2,485 | - |
| **字体加载时间** | ~6s | ~4s | ⚡ -33% |
---
### 2.4 测试验证
**功能测试:**
- [ ] 中文字符正常
- [ ] 英文字符正常
- [ ] 数字正常
- [ ] 标点符号正常
- [ ] 深色模式正常
**性能测试:**
- [ ] Network面板显示字体大小减少
- [ ] 无404错误
- [ ] Lighthouse无字体警告
---
## 🚀 第3步:GitHub Actions自动化
### 3.1 优化目标
实现字体子集化的完全自动化:
- 内容更新时自动优化
- 每周定期检查
- 智能检测变更
- 与deploy.yml完美协调
### 3.2 实施步骤
#### 步骤1:创建requirements.txt
**文件:** `requirements.txt`
```
fonttools
brotli
```
---
#### 步骤2:创建GitHub Actions工作流
**文件:** `.github/workflows/subset-fonts.yml`
**关键配置:**
1. **触发条件:**
```yaml
on:
push:
branches:
- main
paths:
- 'content/**'
- 'layouts/**'
schedule:
- cron: '0 2 * * 1' # 每周一凌晨2点
workflow_dispatch:
inputs:
force_rebuild:
description: '强制重新生成子集字体'
required: false
default: 'false'
type: boolean
```
2. **工作流步骤:**
```yaml
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
pip install fonttools brotli
- name: Build Hugo site
uses: peaceiris/actions-hugo@v2
with:
hugo-version: 'latest'
extended: true
- name: Subset fonts
run: python scripts/subset-font-safe.py
- name: Commit changes
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add themes/Ying/static/font/zql-v2-subset.*
git commit -m "chore: update font subset (automated) [skip ci]"
- name: Push changes
run: git push origin main
```
3. **避免循环触发:**
```yaml
git commit -m "chore: update font subset (automated) [skip ci]"
```
---
#### 步骤3:与deploy.yml协调
**你的deploy.yml已有防循环机制:**
```yaml
- name: Push Image Optimizations
run: |
if ! git log --oneline -1 | grep -q "\[skip ci\]"; then
echo "No auto-commits to push"
else
git push origin HEAD:main
fi
```
**协调流程:**
```
你push到main
↓
deploy.yml(部署文章)
↓
subset-fonts.yml(优化字体)
↓
commit [skip ci]
↓
push到main
↓
deploy.yml看到[skip ci],不再次触发 ✅
↓
完美协调!
```
---
### 3.3 预期效果
**自动化程度:** 100%
**触发条件:**
- ✅ 内容更新时自动优化
- ✅ 每周定期检查
- ✅ 手动触发支持
**协调机制:**
- ✅ 与deploy.yml完美协调
- ✅ 无循环触发
- ✅ 智能检测变更
---
### 3.4 测试验证
**功能测试:**
- [ ] GitHub Actions正常运行
- [ ] 字体优化成功
- [ ] 自动commit和push
- [ ] 与deploy.yml协调正常
**自动化测试:**
- [ ] 发布新文章时触发
- [ ] 每周定时触发
- [ ] 手动触发成功
---
## 📊 完整优化效果
### 性能提升总结
| 优化步骤 | 优化内容 | 提升 |
|---------|---------|------|
| **第1步** | JS按需加载 | ⚡ -56%(首页) |
| **第2步** | 字体子集化 | ⚡ -37% |
| **第3步** | 自动化 | ⚡ 100%自动化 |
| **总计** | 性能优化 | ⚡ -50%(总体) |
### Lighthouse得分
- **优化前:** 60分
- **优化后:** 75-80分
- **提升:** +25-33%
---
## 🎯 实施时间表
### 第1天:JS优化(2小时)
**上午:**
- 分析JS结构
- 修改footer.html
- 测试功能
**下午:**
- 适配PJAX
- 性能测试
- 提交代码
---
### 第2天:字体优化(1.5小时)
**上午:**
- 备份字体
- 安装Python依赖
- 运行子集化
**下午:**
- 更新CSS
- 测试字体显示
- 提交代码
---
### 第3天:自动化(1小时)
**上午:**
- 创建requirements.txt
- 创建GitHub Actions工作流
- 测试自动化
**下午:**
- 验证与deploy.yml协调
- 提交代码
- 监控Actions运行
---
## 💡 最佳实践
### 1. 逐步实施
- ✅ 先实施JS优化
- ✅ 验证无问题后实施字体优化
- ✅ 最后配置自动化
- ✅ 每个步骤都测试验证
### 2. 充分测试
- ✅ 功能测试(所有页面)
- ✅ 性能测试(Lighthouse)
- ✅ 兼容性测试(多浏览器)
- ✅ 自动化测试(GitHub Actions)
### 3. 文档记录
- ✅ 记录所有修改
- ✅ 记录测试结果
- ✅ 记录问题和解决方案
- ✅ 创建故障排除指南
---
## 🔄 回滚方案
### 如果JS优化失败
```bash
# 恢复footer.html
git checkout themes/Ying/layouts/partials/footer.html
# 恢复mypjax.js
git checkout themes/Ying/assets/js/modules/mypjax.js
# 重新构建
hugo --cleanDestinationDir
```
### 如果字体优化失败
```bash
# 恢复字体文件
cp themes/Ying/static/font/zql-v2.woff2.backup themes/Ying/static/font/zql-v2.woff2
cp themes/Ying/static/font/zql-v2.woff.backup themes/Ying/static/font/zql-v2.woff
# 恢复CSS
git checkout themes/Ying/assets/css/main.css
# 重新构建
hugo --cleanDestinationDir
```
### 如果自动化失败
```bash
# 删除工作流文件
rm .github/workflows/subset-fonts.yml
# 或者禁用工作流
# 在GitHub仓库设置中禁用Actions
```
---
## 🎉 项目完成
### 完成情况
- ✅ 第1步:JS按需加载优化(-56%首页)
- ✅ 第2步:字体子集化优化(-37%)
- ✅ 第3步:GitHub Actions自动化(100%)
- ✅ 总体性能提升50%
- ✅ Lighthouse 75-80分
### 后续使用
**什么都不用做!** 🚀
- ✅ 系统已经自动化运行
- ✅ 发布新文章时自动优化
- ✅ 享受性能提升
- ✅ 专注于内容创作
---
**三步优化完成时间:** 2026-06-03
**总耗时:** 约4.5小时
**优化效果:** 性能提升50%
**自动化程度:** 100%
**维护成本:** 0(完全自动化)
**祝你博客越办越好!** 🎉
@@ -0,0 +1,559 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 04-JS按需加载优化
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已完成
**优化效果:** 首页JS减少56%,文章页JS减少44%
---
## 📋 优化概述
### 优化目标
将所有JS打包为单个bundle(800KB)拆分为多个bundle,实现按需加载:
- 核心JS始终加载(~200KB)
- 页面特定JS按需加载(~180KB)
- 非关键JS延迟加载(~25KB)
### 优化效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **首页JS** | 800KB | 350KB | ⚡ -56% |
| **文章页JS** | 800KB | 450KB | ⚡ -44% |
| **TTI** | 4.0s | ~2.0s | ⚡ -50% |
| **TBT** | 350ms | ~100ms | ⚡ -71% |
---
## 🔍 JS文件分析
### 原始JS结构(优化前)
所有JS打包为单个bundle.js(800KB):
```
bundle.js (800KB)
├── iconfont.js
├── uikit.min.js
├── view-image.min.js
├── utils.js
├── loader.js
├── cache.js
├── search.js
├── floating-tools.js
├── paragraph-comments.js
├── infinite-scroll.js
├── artalk.js
├── nprogress.js
├── pjax.js
├── pangu.js
├── linkify.js
├── main.js
├── mypjax.js
├── toast.js
├── tiaozhuan.js
├── reward.js
└── lazyload.js
```
**问题:**
- 所有页面都加载所有JS
- 首页不需要Artalk评论
- 文章页不需要无限滚动
- 浪费带宽和加载时间
---
### 优化后JS结构
拆分为4个bundle:
```
1. core.js (200KB) - 始终加载
├── iconfont.js
├── uikit.min.js
├── view-image.min.js
├── utils.js
├── loader.js
├── cache.js
├── search.js
├── floating-tools.js
├── nprogress.js
├── pjax.js
├── mypjax.js
├── pangu.js
├── linkify.js
└── main.js
2. page-only.js (180KB) - 文章详情页
├── artalk.js
├── paragraph-comments.js
└── reward.js
3. deferred.js (25KB) - 延迟加载
├── toast.js
└── lazyload.js
4. infinite-scroll.js (20KB) - 首页(如果启用)
5. tiaozhuan.js (8KB) - 特定页面
```
---
## 🛠️ 实施步骤
### 步骤1:修改footer.html
**文件:** `themes/Ying/layouts/partials/footer.html`
**修改内容:**
#### 1. 创建核心JS bundle
```gohtml
{{/* ====== 1. 核心JS Bundle - 始终加载 ====== */}}
{{ $iconfont := resources.Get "js/libs/iconfont.js" }}
{{ $uikit := resources.Get "js/libs/uikit.min.js" }}
{{ $viewimage := resources.Get "js/libs/view-image.min.js" }}
{{ $utils := resources.Get "js/modules/utils.js" }}
{{ $loader := resources.Get "js/modules/loader.js" }}
{{ $cache := resources.Get "js/modules/cache.js" }}
{{ $search := resources.Get "js/modules/search.js" }}
{{ $floatingTools := resources.Get "js/modules/floating-tools.js" }}
{{ $nprogress := resources.Get "js/libs/nprogress.js" }}
{{ $pjaxLib := resources.Get "js/libs/pjax.js" }}
{{ $mypjax := resources.Get "js/modules/mypjax.js" }}
{{ $pangu := resources.Get "js/modules/pangu.js" }}
{{ $linkify := resources.Get "js/modules/linkify.js" }}
{{ $main := resources.Get "js/main.js" }}
{{ $coreScripts := slice $iconfont $uikit $viewimage $utils $loader $cache $search $floatingTools $nprogress $pjaxLib $mypjax $pangu $linkify $main | resources.Concat "js/core.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $coreScripts.RelPermalink }}"></script>
```
---
#### 2. 创建页面特定JS bundle
```gohtml
{{/* ====== 4. 页面特定JS - 按需加载 ====== */}}
{{/* 文章详情页专用JS */}}
{{ if .IsPage }}
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
{{ $paragraphComments := resources.Get "js/modules/paragraph-comments.js" }}
{{ $reward := resources.Get "js/modules/reward.js" }}
{{ $pageScripts := slice $artalkModule $paragraphComments $reward | resources.Concat "js/page-only.js" | resources.Minify | resources.Fingerprint }}
<script defer src="{{ $pageScripts.RelPermalink }}"></script>
<script>window._pageOnlyScriptUrl = '{{ $pageScripts.RelPermalink }}';</script>
{{ else }}
{{/* 非文章页面:只存储URL,不加载 */}}
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
{{ $paragraphComments := resources.Get "js/modules/paragraph-comments.js" }}
{{ $reward := resources.Get "js/modules/reward.js" }}
{{ $pageScripts := slice $artalkModule $paragraphComments $reward | resources.Concat "js/page-only.js" | resources.Minify | resources.Fingerprint }}
<script>window._pageOnlyScriptUrl = '{{ $pageScripts.RelPermalink }}';</script>
{{ end }}
```
---
#### 3. 创建延迟加载JS bundle
```gohtml
{{/* ====== 5. 延迟加载的非关键JS ====== */}}
{{ $toast := resources.Get "js/modules/toast.js" }}
{{ $easylazyload := resources.Get "js/modules/lazyload.js" }}
{{ $deferredScripts := slice $toast $easylazyload | resources.Concat "js/deferred.js" | resources.Minify | resources.Fingerprint }}
<script>
// 使用requestIdleCallback在浏览器空闲时加载
if ('requestIdleCallback' in window) {
requestIdleCallback(function() {
var script = document.createElement('script');
script.src = '{{ $deferredScripts.RelPermalink }}';
script.defer = true;
document.body.appendChild(script);
});
} else {
setTimeout(function() {
var script = document.createElement('script');
script.src = '{{ $deferredScripts.RelPermalink }}';
script.defer = true;
document.body.appendChild(script);
}, 1000);
}
</script>
```
---
#### 4. 首页专用JS
```gohtml
{{/* ====== 6. 首页专用JS ====== */}}
{{ if .IsHome }}
{{ if .Site.Params.infiniteScroll.enable }}
{{ $infiniteScroll := resources.Get "js/modules/infinite-scroll.js" }}
<script defer src="{{ $infiniteScroll.RelPermalink }}"></script>
{{ end }}
{{ end }}
```
---
#### 5. 特定页面专用JS
```gohtml
{{/* ====== 7. 特定页面专用JS ====== */}}
{{ if or (eq .Type "links") (eq .Type "circles") }}
{{ $tiaozhuan := resources.Get "js/modules/tiaozhuan.js" }}
<script defer src="{{ $tiaozhuan.RelPermalink }}"></script>
{{ end }}
```
---
### 步骤2:适配PJAX
**文件:** `themes/Ying/assets/js/modules/mypjax.js`
**修改内容:**
在 `pjax:complete` 事件中添加动态加载逻辑:
```javascript
// pjax加载完成
document.addEventListener("pjax:complete", function () {
// 立即执行:进度条、基础 UI
if (typeof NProgress !== 'undefined') NProgress.done();
pjax_reload();
// 动态加载页面特定JS(PJAX适配)
// 检测当前页面是否为文章详情页,如果是则加载page-only.js
var currentPath = window.location.pathname;
var isArticlePage = document.querySelector('#Comments') !== null ||
document.querySelector('.post-content') !== null;
if (isArticlePage && !window._pageOnlyLoaded && window._pageOnlyScriptUrl) {
// 动态加载page-only.js(使用Hugo fingerprint后的正确URL)
var script = document.createElement('script');
script.src = window._pageOnlyScriptUrl;
script.onload = function() {
window._pageOnlyLoaded = true;
console.log('page-only.js loaded for PJAX navigation');
// 初始化Artalk等
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) { console.error('initArtalk error:', e); }
}
if (typeof window.initParagraphComments === 'function') {
try { window.initParagraphComments(); } catch(e) {}
}
};
document.body.appendChild(script);
}
// 下一帧执行:轻量初始化
requestAnimationFrame(function() {
if (typeof initCodeCopy === 'function') initCodeCopy();
if (typeof window.initSearch === 'function') {
try { window.initSearch(); } catch(e) {}
}
if (typeof window.refreshFloatingTools === 'function') {
window.refreshFloatingTools();
} else if (typeof window.initFloatingTools === 'function') {
try { window.initFloatingTools(); } catch(e) {}
}
// 文章动画
if (typeof window.initPostScrollspy === 'function') window.initPostScrollspy();
});
// 空闲时执行:非关键功能
var idle = window.requestIdleCallback || function(fn) { return setTimeout(fn, 200); };
idle(function() {
if (typeof window.initParagraphComments === 'function') {
try { window.initParagraphComments(); } catch(e) {}
}
if (typeof window.initInfiniteScroll === 'function' && window.enableInfiniteScroll) {
window.initInfiniteScroll();
}
if (typeof window.initLinkStatus === 'function') {
try { window.initLinkStatus(); } catch(e) {}
}
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) {}
}
if (typeof window.bsz_fetch === 'function') {
try { window.bsz_fetch(); } catch(e) {}
}
if (typeof window.initImageFrameReveal === 'function') {
try { window.initImageFrameReveal(); } catch(e) {}
}
// 链接卡片和中英文间距(DOM 扫描,非关键)
if (typeof window.initLinkify === 'function') window.initLinkify();
if (typeof window.initPangu === 'function') window.initPangu();
});
// 延迟:代码折叠
setTimeout(function() {
if (typeof window.initCodeFold === 'function') window.initCodeFold();
}, 300);
});
```
---
## 📊 加载时序
### 优化前
```
页面加载
↓
下载bundle.js (800KB)
↓
执行所有JS
↓
渲染页面
↓
用户可以交互
```
**问题:** 所有JS都在首屏加载,阻塞渲染
---
### 优化后
```
页面加载
↓
下载core.js (200KB) - 立即
↓
渲染页面(核心功能可用)
↓
用户可以交互
↓
下载page-only.js (180KB) - 按需(仅文章页)
↓
下载deferred.js (25KB) - 延迟(浏览器空闲)
↓
所有功能可用
```
**优势:**
- ✅ 首屏渲染更快
- ✅ 核心功能立即可用
- ✅ 非核心功能延迟加载
- ✅ 节省带宽
---
## 🧪 测试验证
### 功能测试清单
#### 首页功能
- [ ] 导航菜单正常
- [ ] 搜索功能正常
- [ ] 主题切换正常
- [ ] 文章列表显示正常
- [ ] 分页功能正常
- [ ] 无限滚动正常(如果启用)
- [ ] 浮动工具栏正常
#### 文章详情页功能
- [ ] 文章内容正常显示
- [ ] 图片灯箱正常
- [ ] 评论区正常加载(Artalk)
- [ ] 打赏功能正常
- [ ] 段落评论正常
- [ ] 返回顶部正常
#### 其他页面功能
- [ ] 友链页面正常
- [ ] circles页面正常
- [ ] 归档页面正常
- [ ] 搜索结果页正常
#### 跨页面功能
- [ ] PJAX导航正常
- [ ] 浏览器前进/后退正常
- [ ] 书签/分享链接正常
---
### 性能测试
#### Network面板
1. 打开DevTools → Network
2. 刷新页面
3. 检查:
- [ ] core.js首先加载(~200KB)
- [ ] page-only.js仅在文章页加载(~180KB)
- [ ] deferred.js最后加载(~25KB)
- [ ] 总体JS大小减少
#### Lighthouse测试
1. 打开DevTools → Lighthouse
2. 运行Performance审计
3. 预期指标:
- [ ] Performance得分:75-85
- [ ] TTI:改善20-30%
- [ ] TBT:改善40-50%
- [ ] Speed Index:改善20-30%
---
## 🐛 故障排除
### 问题1:评论区未加载
**症状:** 文章详情页看不到评论区
**可能原因:**
1. page-only.js加载失败
2. Artalk初始化时机不对
3. JavaScript错误
**解决方案:**
1. 打开Console查看错误
2. 检查Network面板,确认page-only.js加载成功
3. 确认window._pageOnlyScriptUrl已定义
---
### 问题2:功能延迟响应
**症状:** 点击某些按钮后1-2秒才响应
**原因:** 非关键JS还在加载
**解决方案:**
- 这是预期行为,用户可能会感觉到轻微延迟
- 如果延迟明显(>3秒),考虑将该模块移到core.js
---
### 问题3:无限滚动失效
**症状:** 首页无法加载更多文章
**可能原因:**
1. infinite-scroll.js未加载
2. window.enableInfiniteScroll未定义
**解决方案:**
1. 检查hugo.toml中infiniteScroll.enable是否为true
2. 查看Console是否有错误
3. 确认infinite-scroll.js加载成功
---
### 问题4:PJAX导航失效
**症状:** 点击链接后页面完全刷新
**可能原因:**
1. PJAX库未加载
2. mypjax.js初始化失败
**解决方案:**
1. 检查Console是否有错误
2. 确认pjax.js在core.js中
3. 检查mypjax.js的配置
---
## 🔄 回滚方案
### 如果优化后出现严重问题
**快速回滚:**
```bash
cd E:\GitHub\blog
# 恢复footer.html
git checkout themes/Ying/layouts/partials/footer.html
# 恢复mypjax.js
git checkout themes/Ying/assets/js/modules/mypjax.js
# 重新构建
hugo --cleanDestinationDir
hugo server -D
```
---
## 💡 最佳实践
### 1. 逐步优化
- ✅ 先测试核心功能
- ✅ 逐步添加按需加载
- ✅ 充分测试每个步骤
- ✅ 记录问题和解决方案
### 2. 监控性能
- ✅ 定期Lighthouse测试
- ✅ 监控网络请求
- ✅ 检查Console错误
- ✅ 记录性能数据
### 3. 持续改进
- ✅ 根据实际使用调整
- ✅ 优化加载时序
- ✅ 减少bundle大小
- ✅ 提升用户体验
---
## 📈 优化效果总结
### 性能提升
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **首页JS** | 800KB | 350KB | ⚡ -56% |
| **文章页JS** | 800KB | 450KB | ⚡ -44% |
| **TTI** | 4.0s | ~2.0s | ⚡ -50% |
| **TBT** | 350ms | ~100ms | ⚡ -71% |
### 用户体验提升
- 🚀 **首屏更快** - 资源减少56%
- ⚡ **交互更流畅** - TTI提升50%
- 📱 **移动端更好** - 节省带宽
- 🎨 **功能完整** - 所有功能正常
---
**JS优化完成时间:** 2026-06-03
**实施耗时:** 约2小时
**优化效果:** 首页JS减少56%
**风险等级:** 低
**维护成本:** 低
**JS优化效果显著!** 🎉
@@ -0,0 +1,330 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 05-PJAX适配说明
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已完成
**适用范围:** JS按需加载优化的PJAX适配
---
## 📋 问题背景
### PJAX是什么?
PJAX(PushState + AJAX)是一种页面无刷新加载技术:
- 通过AJAX加载新内容
- 更新浏览器地址栏
- 无需刷新整个页面
- 提升用户体验
### 问题场景
**优化前:**
```
用户访问首页 → bundle.js加载(800KB)
用户点击文章 → PJAX导航(无刷新)
进入文章详情页 → 评论区正常(因为所有JS已加载)
```
**优化后(有问题):**
```
用户访问首页 → core.js加载(200KB)
用户点击文章 → PJAX导航(无刷新)
进入文章详情页 → 评论区不加载 ❌
→ page-only.js未加载 ❌
→ initArtalk未定义 ❌
```
**根本原因:**
- PJAX不重新加载JS文件
- page-only.js只在首次访问时加载
- PJAX导航时,JS已经加载过了,不会重新加载
---
## ✅ 解决方案
### 方案:动态加载 + 全局URL
**实现思路:**
1. **存储URL** - 在footer.html中存储page-only.js的URL到全局变量
2. **检测页面** - 在mypjax.js中检测当前页面是否为文章详情页
3. **动态加载** - 如果是且page-only.js未加载,动态创建script标签加载
4. **初始化功能** - 加载完成后自动初始化Artalk等功能
---
## 🛠️ 实施步骤
### 步骤1:修改footer.html
**修改内容:**
- 所有页面都计算page-only.js的URL
- 存储在window._pageOnlyScriptUrl全局变量中
- 文章页面直接加载,非文章页面只存储URL
**关键代码:**
```gohtml
{{ if .IsPage }}
{{/* 文章页面:加载page-only.js */}}
<script defer src="{{ $pageScripts.RelPermalink }}"></script>
<script>window._pageOnlyScriptUrl = '{{ $pageScripts.RelPermalink }}';</script>
{{ else }}
{{/* 非文章页面:只存储URL,不加载 */}}
<script>window._pageOnlyScriptUrl = '{{ $pageScripts.RelPermalink }}';</script>
{{ end }}
```
**作用:**
- 确保PJAX导航时能找到page-only.js的正确路径
- Hugo会自动添加fingerprint(如page-only.min.abc123.js)
---
### 步骤2:修改mypjax.js
**修改内容:**
- 在pjax:complete事件中添加动态加载逻辑
- 检测当前页面是否为文章详情页
- 如果是且page-only.js未加载,动态加载
- 设置window._pageOnlyLoaded标志防止重复加载
**关键代码:**
```javascript
// 动态加载页面特定JS(PJAX适配)
var isArticlePage = document.querySelector('#Comments') !== null ||
document.querySelector('.post-content') !== null;
if (isArticlePage && !window._pageOnlyLoaded && window._pageOnlyScriptUrl) {
var script = document.createElement('script');
script.src = window._pageOnlyScriptUrl;
script.onload = function() {
window._pageOnlyLoaded = true;
console.log('page-only.js loaded for PJAX navigation');
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) {}
}
if (typeof window.initParagraphComments === 'function') {
try { window.initParagraphComments(); } catch(e) {}
}
};
document.body.appendChild(script);
}
```
**作用:**
- 当PJAX导航到文章页时,自动加载page-only.js
- 初始化Artalk评论、段落评论、打赏功能
- 确保用户体验无缝
---
## 🧪 测试验证
### 测试场景
#### 场景1:首页 → 文章详情页
**步骤:**
1. 访问首页
2. 点击文章链接
3. 检查文章详情页
**预期结果:**
- ✅ PJAX导航成功(地址栏更新,无刷新)
- ✅ 文章内容正常显示
- ✅ 评论区正常加载
- ✅ Console显示:`page-only.js loaded for PJAX navigation`
---
#### 场景2:文章 → 另一篇文章
**步骤:**
1. 在文章详情页
2. 点击"下一篇"或其他文章
3. 检查新文章页
**预期结果:**
- ✅ PJAX导航成功
- ✅ 新文章内容正常
- ✅ 评论区正常(无需重新加载page-only.js)
---
#### 场景3:文章 → 首页
**步骤:**
1. 在文章详情页
2. 点击导航栏"首页"
3. 检查首页
**预期结果:**
- ✅ PJAX导航成功
- ✅ 首页内容正常
- ✅ 无Console错误
---
### Console日志检查
**正常情况应该看到:**
**访问首页时:**
```
Pjax initialized: {...}
(无page-only.js相关日志)
```
**PJAX导航到文章详情页时:**
```
Pjax reload triggered
page-only.js loaded for PJAX navigation
```
---
## 💡 技术细节
### 为什么需要全局URL?
**问题:** Hugo构建时会自动添加fingerprint
**示例:**
```
原始:page-only.js
构建后:page-only.min.abc123.js
```
**解决:** 使用全局变量存储正确的URL
```javascript
window._pageOnlyScriptUrl = '/js/page-only.min.abc123.js';
```
---
### 如何防止重复加载?
**使用标志位:**
```javascript
// 检查是否已加载
if (!window._pageOnlyLoaded) {
// 加载page-only.js
// ...
window._pageOnlyLoaded = true;
}
```
---
### 错误处理
**如果page-only.js加载失败:**
```javascript
script.onerror = function() {
console.error('Failed to load page-only.js');
// 可以尝试重新加载或显示错误提示
};
```
---
## 📊 性能影响
### 首次加载(首页)
- ✅ page-only.js不加载(节省~180KB)
- ✅ 首页加载更快
### PJAX导航到文章详情页
- ⚠️ 需要额外加载page-only.js(~180KB)
- ⚠️ 会有100-200ms延迟(网络请求)
- ✅ 但这是按需加载,用户正在看文章,可以接受
### 后续PJAX导航(文章→文章)
- ✅ page-only.js已加载,无需重新加载
- ✅ 性能无影响
---
## 🔄 回滚方案
### 如果PJAX适配出现问题
**方案1:回滚mypjax.js**
```bash
git checkout themes/Ying/assets/js/modules/mypjax.js
```
**方案2:始终加载page-only.js**
修改footer.html,所有页面都加载page-only.js:
```gohtml
{{/* 始终加载page-only.js */}}
<script defer src="{{ $pageScripts.RelPermalink }}"></script>
```
**缺点:** 首页也会加载artalk等JS,违背优化初衷
---
## 💡 最佳实践
### 1. 充分测试
- ✅ 测试所有页面类型
- ✅ 测试PJAX导航场景
- ✅ 测试边界情况
- ✅ 监控Console错误
### 2. 性能监控
- ✅ 监控page-only.js加载时间
- ✅ 检查是否有重复加载
- ✅ 记录PJAX导航耗时
- ✅ 优化加载时序
### 3. 错误处理
- ✅ 添加加载失败处理
- ✅ 提供降级方案
- ✅ 记录错误日志
- ✅ 及时修复问题
---
## 📈 总结
### PJAX适配完成
- ✅ 动态加载page-only.js
- ✅ 全局变量传递URL
- ✅ 事件监听(pjax:complete)
- ✅ 加载状态标志(防止重复加载)
### 优化效果保持
- ✅ 首页JS减少56%
- ✅ 文章页JS减少44%
- ✅ PJAX完美适配
- ✅ 所有功能正常
### 兼容性
- ✅ Chrome 47+
- ✅ Firefox 55+
- ✅ Safari 12.1+
- ✅ Edge 79+
---
**PJAX适配完成时间:** 2026-06-03
**实施耗时:** 约30分钟
**风险等级:** 低
**测试状态:** 通过
**PJAX适配完美!** 🎉
@@ -0,0 +1,204 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 06-PJAX修复总结
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已完成
**问题:** PJAX导航后评论区不加载
---
## 🔴 问题描述
### 问题现象
用户反馈:通过PJAX导航到文章详情页后,评论区不加载。
### 复现步骤
1. 访问首页
2. 点击文章链接(PJAX导航,无刷新)
3. 进入文章详情页
4. 评论区未加载
### 错误信息
Console显示:
```
Uncaught ReferenceError: initArtalk is not defined
```
---
## 🔍 问题分析
### 根本原因
1. **page-only.js只在首次访问时加载**
- 文章详情页直接访问时,page-only.js在footer.html中加载
- PJAX导航时,不会重新加载JS文件
2. **initArtalk函数未定义**
- artalk.js中的initArtalk函数在page-only.js中
- 如果page-only.js未加载,该函数不存在
3. **mypjax.js尝试调用未定义的函数**
- pjax:complete事件中调用initArtalk
- 但函数未定义,导致错误
---
## ✅ 解决方案
### 方案:动态加载 + 全局URL
**核心思路:**
1. 存储page-only.js的URL到全局变量
2. PJAX导航时检测是否需要加载
3. 动态创建script标签加载
4. 加载完成后初始化功能
---
## 🛠️ 实施步骤
### 步骤1:修改footer.html
**修改内容:**
- 所有页面都计算page-only.js的URL
- 存储在window._pageOnlyScriptUrl全局变量
**关键代码:**
```gohtml
{{ if .IsPage }}
<script defer src="{{ $pageScripts.RelPermalink }}"></script>
<script>window._pageOnlyScriptUrl = '{{ $pageScripts.RelPermalink }}';</script>
{{ else }}
<script>window._pageOnlyScriptUrl = '{{ $pageScripts.RelPermalink }}';</script>
{{ end }}
```
---
### 步骤2:修改mypjax.js
**修改内容:**
- 在pjax:complete事件中添加动态加载逻辑
- 检测当前页面是否为文章详情页
- 动态加载page-only.js
- 初始化Artalk等功能
**关键代码:**
```javascript
// 动态加载页面特定JS(PJAX适配)
var isArticlePage = document.querySelector('#Comments') !== null ||
document.querySelector('.post-content') !== null;
if (isArticlePage && !window._pageOnlyLoaded && window._pageOnlyScriptUrl) {
var script = document.createElement('script');
script.src = window._pageOnlyScriptUrl;
script.onload = function() {
window._pageOnlyLoaded = true;
console.log('page-only.js loaded for PJAX navigation');
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) {}
}
};
document.body.appendChild(script);
}
```
---
## 🧪 测试验证
### 测试场景
**场景1:首页 → 文章详情页**
- [ ] PJAX导航成功
- [ ] 评论区正常加载
- [ ] Console显示加载日志
**场景2:文章 → 另一篇文章**
- [ ] PJAX导航成功
- [ ] 评论区正常(无需重新加载)
**场景3:文章 → 首页**
- [ ] PJAX导航成功
- [ ] 首页正常
---
## 📊 性能影响
### 优化效果保持
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **首页JS** | 800KB | 350KB | ⚡ -56% |
| **文章页JS** | 800KB | 450KB | ⚡ -44% |
| **PJAX适配** | ✅ | ✅ | - |
### PJAX适配开销
- **代码量:** +30行(mypjax.js)
- **运行时开销:** 可忽略不计
- **网络开销:** 仅首次加载page-only.js(~180KB)
---
## 🔄 回滚方案
### 如果修复出现问题
**方案1:回滚mypjax.js**
```bash
git checkout themes/Ying/assets/js/modules/mypjax.js
```
**方案2:始终加载page-only.js**
```gohtml
<script defer src="{{ $pageScripts.RelPermalink }}"></script>
```
---
## 💡 经验总结
### 关键点
1. **PJAX不重新加载JS**
- 需要手动处理动态加载
- 使用全局变量传递URL
2. **函数定义检查**
- 调用前检查函数是否存在
- 使用typeof检查
3. **加载状态管理**
- 使用标志位防止重复加载
- window._pageOnlyLoaded
4. **错误处理**
- 添加try-catch
- 记录错误日志
---
## ✅ 修复完成
- ✅ PJAX导航正常
- ✅ 评论区正常加载
- ✅ 性能优化保持
- ✅ 所有功能正常
**PJAX修复成功!** 🎉
---
**修复完成时间:** 2026-06-03
**修复耗时:** 约30分钟
**风险等级:** 低
**测试状态:** 通过
@@ -0,0 +1,239 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 07-字体子集化优化
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已完成
**优化效果:** 字体减少37%(1.2MB → 757KB)
---
## 📋 优化概述
### 优化目标
将中文字体从1.2MB优化到757KB,减少37%,提升加载速度。
### 优化效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **字体大小** | 1.2MB | 757KB | ⚡ -37% |
| **字符数** | 20,000+ | 2,485 | - |
| **加载时间** | ~6s | ~4s | ⚡ -33% |
---
## 🔍 优化原理
### 原始字体
- 包含完整中文字符集(20,000+字符)
- 包含CJK扩展区(生僻字)
- 文件大小:1.2MB
### 子集字体
- 只包含实际使用的字符(2,485个)
- 常用中文字符
- 英文字母和数字
- 常用标点符号
- 文件大小:757KB
---
## 🛠️ 实施步骤
### 步骤1:备份原始字体
```bash
cd E:\GitHub\blog
# Windows
copy themes\Ying\static\font\zql-v2.woff2 themes\Ying\static\font\zql-v2.woff2.backup
copy themes\Ying\static\font\zql-v2.woff themes\Ying\static\font\zql-v2.woff.backup
# Mac/Linux
cp themes/Ying/static/font/zql-v2.woff2 themes/Ying/static/font/zql-v2.woff2.backup
cp themes/Ying/static/font/zql-v2.woff themes/Ying/static/font/zql-v2.woff.backup
```
---
### 步骤2:安装Python依赖
```bash
pip install fonttools brotli
```
---
### 步骤3:构建Hugo站点
```bash
hugo --destination=public
```
---
### 步骤4:运行字体子集化
```bash
python scripts/subset-font-safe.py
```
**预期输出:**
```
🔤 字体子集化工具(安全版本)
==================================================
✅ 找到public目录,将扫描构建后的HTML
🔍 扫描目录: content, layouts, public
📝 提取了 2492 个唯一字符
💾 字符列表已保存到: themes/Ying/static/font\used_chars.txt
✂️ 正在生成子集字体...
✅ 子集化完成!
📊 优化结果:
子集字符数: 2485
子集文件大小: 739.7 KB
减少: 486.8 KB (39.7%)
🎉 所有子集字体生成成功!
```
---
### 步骤5:更新CSS字体声明
**文件:** `themes/Ying/assets/css/main.css`
**修改字体声明:**
```css
@font-face {
font-family: 'zql';
src: url('../font/zql-v2-subset.woff2') format('woff2'),
url('../font/zql-v2-subset.woff') format('woff');
font-display: swap;
}
```
**关键改动:**
- `zql-v2.woff2` → `zql-v2-subset.woff2`
- `zql-v2.woff` → `zql-v2-subset.woff`
- 删除 `unicode-range`
---
## 🧪 测试验证
### 功能测试
- [ ] 中文字符正常
- [ ] 英文字符正常
- [ ] 数字正常
- [ ] 标点符号正常
- [ ] 深色模式正常
- [ ] 移动端正常
### 性能测试
- [ ] Network面板显示字体大小减少
- [ ] 无404错误
- [ ] Lighthouse无字体警告
---
## 🔄 回滚方案
### 如果优化失败
```bash
# 恢复原始字体
cp themes/Ying/static/font/zql-v2.woff2.backup themes/Ying/static/font/zql-v2.woff2
cp themes/Ying/static/font/zql-v2.woff.backup themes/Ying/static/font/zql-v2.woff
# 恢复CSS
git checkout themes/Ying/assets/css/main.css
```
---
## 📊 Python脚本说明
### 脚本功能
**subset-font-safe.py:**
- 扫描所有HTML、CSS、Markdown文件
- 提取实际使用的字符
- 生成子集字体
- 验证优化效果
### 工作原理
1. **扫描目录**
- content/(文章内容)
- layouts/(模板文件)
- public/(构建后的HTML)
2. **提取字符**
- 中文字符
- 英文字母和数字
- 常用标点符号
- 特殊符号
3. **生成子集**
- 使用fonttools库
- 保留字体特性
- 压缩输出
---
## 💡 最佳实践
### 1. 定期更新
```bash
# 每月运行一次
python scripts/subset-font-safe.py
```
### 2. 监控字符覆盖
```bash
# 查看字符列表
cat themes/Ying/static/font/used_chars.txt
```
### 3. 验证优化效果
```bash
# 检查文件大小
ls -lh themes/Ying/static/font/zql-v2-subset.*
```
---
## 📈 优化总结
### 完成情况
- ✅ 字体大小减少37%
- ✅ 保持所有字符正常显示
- ✅ 加载速度提升33%
- ✅ 自动化脚本就绪
### 后续使用
- ✅ GitHub Actions自动优化
- ✅ 内容更新时自动触发
- ✅ 无需手动干预
---
**字体优化完成时间:** 2026-06-03
**实施耗时:** 约1.5小时
**优化效果:** 字体减少37%
**风险等级:** 低
**维护成本:** 0(自动化)
**字体优化效果显著!** 🎉
@@ -0,0 +1,359 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 08-GitHub-Actions使用指南
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已配置完成
**工作流:** Font Subset Optimization
---
## 📋 工作流概述
### 工作流名称
`Font Subset Optimization`(字体子集优化)
### 工作流文件
`.github/workflows/subset-fonts.yml`
### 触发条件
1. **自动触发** - 推送到main分支且content/或layouts/有变更
2. **手动触发** - 在GitHub Actions界面手动运行
3. **定期触发** - 每周一凌晨2点自动检查
---
## 🚀 使用方法
### 方法1:自动触发(推荐)✅
**无需任何操作!** 当你推送内容更新时,工作流自动运行:
```bash
# 发布新文章
git add content/posts/new-article.md
git commit -m "feat: new article"
git push origin main
# GitHub Actions自动:
# 1. 检测到content目录有变更
# 2. 构建Hugo站点
# 3. 运行字体子集化
# 4. 提交优化后的字体
```
**查看运行状态:**
```
访问:https://github.com/zqlit/blog/actions
```
---
### 方法2:手动触发
**适用场景:**
- 强制重新生成子集字体
- 修改了字体脚本
- 测试工作流
**操作步骤:**
1. 访问GitHub仓库 → **Actions** 标签
2. 选择 **Font Subset Optimization**
3. 点击 **Run workflow**
4. (可选)勾选 **强制重新生成子集字体**
5. 点击 **Run workflow** 按钮
---
### 方法3:定期自动运行
**默认:** 每周一凌晨2点自动运行
**作用:** 检查是否需要更新
**修改频率:**
```yaml
# 编辑 .github/workflows/subset-fonts.yml
schedule:
# 每天凌晨3点
- cron: '0 3 * * *'
# 每月1号凌晨2点
- cron: '0 2 1 * *'
```
---
## 📊 工作流步骤
### 步骤1:检出代码
```yaml
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
```
**作用:** 下载仓库代码
---
### 步骤2:设置Python环境
```yaml
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
```
**作用:** 安装Python 3.11
---
### 步骤3:安装依赖
```yaml
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install fonttools brotli
```
**作用:** 安装字体处理工具
---
### 步骤4:构建Hugo站点
```yaml
- name: Build Hugo site
uses: peaceiris/actions-hugo@v2
with:
hugo-version: 'latest'
extended: true
- name: Build
run: hugo --destination=public --minify
```
**作用:** 生成静态HTML
---
### 步骤5:运行字体子集化
```yaml
- name: Subset fonts
run: python scripts/subset-font-safe.py
```
**作用:** 提取字符并生成优化字体
---
### 步骤6:验证优化效果
```yaml
- name: Verify optimization
run: |
ORIGINAL_SIZE=$(stat -c%s themes/Ying/static/font/zql-v2.woff2)
SUBSET_SIZE=$(stat -c%s themes/Ying/static/font/zql-v2-subset.woff2)
# 检查子集字体是否更小
```
**作用:** 确保优化有效
---
### 步骤7:提交更改
```yaml
- name: Commit changes
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add themes/Ying/static/font/zql-v2-subset.*
git commit -m "chore: update font subset (automated) [skip ci]"
```
**作用:** 保存优化后的字体
**关键:** `[skip ci]` 避免触发deploy.yml
---
### 步骤8:推送更改
```yaml
- name: Push changes
run: git push origin main
```
**作用:** 推送到GitHub仓库
---
## 🔍 监控和调试
### 查看运行状态
1. 访问GitHub仓库
2. 点击 **Actions** 标签
3. 查看运行列表
**状态图标:**
- ✅ **绿色** - 成功
- ❌ **红色** - 失败
- 🟡 **黄色** - 进行中
---
### 查看详细日志
1. 点击具体运行记录
2. 点击 **subset-fonts** 任务
3. 展开每个步骤查看日志
**关键日志:**
```
✅ Font optimized: reduced 486800 bytes (39%)
```
---
## ⚙️ 自定义配置
### 修改触发条件
**只在特定文件变更时触发:**
```yaml
on:
push:
paths:
- 'content/posts/**'
- 'content/**/*.md'
```
---
### 修改运行频率
```yaml
schedule:
# 每天凌晨3点
- cron: '0 3 * * *'
# 每周一和周四凌晨2点
- cron: '0 2 * * 1,4'
```
---
### 禁用定期运行
```yaml
# schedule:
# - cron: '0 2 * * 1'
```
---
## 🐛 故障排除
### 问题1:工作流没有触发
**解决方案:**
1. 检查仓库设置 → Actions → 已启用
2. 检查路径过滤是否正确
3. 查看Actions页面的错误信息
---
### 问题2:Python依赖安装失败
**解决方案:**
```yaml
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install fonttools brotli --no-cache-dir
```
---
### 问题3:Hugo构建失败
**解决方案:**
1. 检查hugo.toml配置
2. 确保所有主题文件存在
3. 查看Hugo错误日志
---
### 问题4:推送失败
**原因:** GitHub Actions没有写权限
**解决方案:**
1. 仓库设置 → Actions → General
2. **Workflow permissions** → 选择 **Read and write permissions**
---
## 💡 最佳实践
### 1. 保护主分支
**建议:** 启用分支保护规则
---
### 2. 监控工作流
**建议:** 设置失败通知
---
### 3. 测试工作流
**建议:** 在feature分支测试
---
## 📈 工作流优势
### ✅ 自动化
- 无需手动运行脚本
- 内容更新时自动优化
- 定期检查确保最新
### ✅ 智能化
- 检测内容变更
- 验证优化效果
- 避免不必要的提交
### ✅ 可靠性
- 使用官方GitHub Actions
- 完整的错误处理
- 详细的日志记录
---
**GitHub Actions配置完成!** 🎉
**现在可以:**
- ✅ 推送内容时自动优化字体
- ✅ 每周定期检查
- ✅ 手动触发(需要时)
- ✅ 无需手动干预
@@ -0,0 +1,210 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 09-Actions修复指南
**创建日期:** 2026-06-03
**版本:** v1.0
**状态:** ✅ 已修复
**适用范围:** GitHub Actions常见问题
---
## 🔴 问题1:Python依赖安装失败
### 错误信息
```
Error: No file in /home/runner/work/blog/blog matched to [**/requirements.txt or **/pyproject.toml]
```
### 原因
actions/setup-python@v5的cache功能需要requirements.txt文件
### 解决方案
**创建requirements.txt:**
```bash
echo fonttools > requirements.txt
echo brotli >> requirements.txt
```
**恢复cache配置:**
```yaml
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
```
---
## 🔴 问题2:与deploy.yml冲突
### 问题场景
```
subset-fonts.yml push到main
↓
触发deploy.yml
↓
deploy.yml可能又push
↓
再次触发subset-fonts.yml
↓
无限循环!❌
```
### 解决方案
**在subset-fonts.yml的commit消息中添加[skip ci]:**
```yaml
git commit -m "chore: update font subset (automated) [skip ci]"
```
**原理:**
- deploy.yml检查commit消息
- 如果包含[skip ci],不会再次触发
- 避免循环触发
---
## 🔴 问题3:工作流没有触发
### 可能原因
1. Actions未启用
2. 路径过滤不正确
3. 仓库名配置错误
### 解决方案
**检查仓库设置:**
```
Settings → Actions → General → 选择 "Allow all actions"
```
**检查仓库名:**
```yaml
if: github.repository == 'zqlit/blog' # 确保正确
```
---
## 🔴 问题4:推送失败
### 错误信息
```
Permission denied
```
### 解决方案
**修改仓库权限:**
```
Settings → Actions → General → Workflow permissions
→ 选择 "Read and write permissions"
→ 勾选 "Allow GitHub Actions to create and approve pull requests"
```
---
## 🔴 问题5:Hugo构建失败
### 可能原因
1. hugo.toml配置错误
2. 主题文件缺失
3. Hugo版本不兼容
### 解决方案
**检查配置文件:**
```bash
hugo config
```
**指定Hugo版本:**
```yaml
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.128.2' # 指定版本
extended: true
```
---
## 🔴 问题6:字体子集化失败
### 可能原因
1. Python脚本语法错误
2. 字体文件不存在
3. 依赖版本不兼容
### 解决方案
**检查Python脚本:**
```bash
python scripts/subset-font-safe.py
```
**检查依赖版本:**
```bash
pip show fonttools
pip show brotli
```
---
## 💡 预防措施
### 1. 定期检查
```bash
# 每周查看Actions运行状态
# https://github.com/zqlit/blog/actions
```
### 2. 监控日志
```bash
# 查看详细日志
# Actions → 具体运行 → subset-fonts → 查看日志
```
### 3. 测试工作流
```bash
# 在feature分支测试
git checkout -b test/workflow
git push origin test/workflow
```
---
## 📞 获取帮助
### 查看GitHub文档
- [GitHub Actions文档](https://docs.github.com/en/actions)
- [workflow语法](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
### 查看Actions日志
```
Actions → 具体运行 → 查看详细日志
```
---
**故障排除指南完成!** 🎉
**遇到问题时:**
1. 查看本文档
2. 检查Actions日志
3. 搜索GitHub文档
@@ -0,0 +1,313 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 10-提交指南
**创建日期:** 2026-06-03
**版本:** v1.0
**适用范围:** 性能优化代码提交
---
## 📋 提交内容清单
### 1. JS优化(已提交)✅
**修改文件:**
- `themes/Ying/layouts/partials/footer.html`
- `themes/Ying/assets/js/modules/mypjax.js`
**提交信息:**
```
perf: JS按需加载优化 + PJAX适配
- 首页JS减少56%(800KB → 350KB)
- 文章页JS减少44%(800KB → 450KB)
- 完美适配PJAX导航
- 所有功能正常
```
---
### 2. 字体优化(待提交)⏳
**修改文件:**
- `themes/Ying/assets/css/main.css`
- `themes/Ying/static/font/zql-v2-subset.woff2`
- `themes/Ying/static/font/zql-v2-subset.woff`
- `themes/Ying/static/font/used_chars.txt`
**提交信息:**
```
perf: 字体子集化优化 - 减少37%
- 字体大小:1.2MB → 757KB
- 使用Python fonttools提取2,485个字符
- 保持所有字符正常显示
```
---
### 3. 自动化工作流(待提交)⏳
**创建文件:**
- `.github/workflows/subset-fonts.yml`
- `requirements.txt`
**提交信息:**
```
ci: 添加字体子集化自动化工作流
- GitHub Actions自动运行
- 内容更新时自动触发
- 每周定期检查
- 智能检测变更
```
---
## 🚀 推荐提交命令
### 方案A:一次提交所有优化(推荐)
```bash
cd E:\GitHub\blog
# 查看修改
git status
# 添加所有文件
git add themes/Ying/assets/css/main.css
git add themes/Ying/static/font/zql-v2-subset.*
git add themes/Ying/static/font/used_chars.txt
git add .github/workflows/subset-fonts.yml
git add requirements.txt
# 提交
git commit -m "perf: 完整性能优化 - JS按需加载 + 字体子集化 + 自动化
JS优化:
- 首页JS减少56%(800KB → 350KB)
- 文章页JS减少44%(800KB → 450KB)
- 完美适配PJAX导航
字体优化:
- 字体大小减少37%(1.2MB → 757KB)
- 使用Python fonttools提取2,485个字符
自动化:
- GitHub Actions自动字体子集化
- 内容更新时自动触发
- 每周定期检查
总体效果:
- 总体资源减少50%+
- Lighthouse得分提升至75-80"
# 推送
git push origin main
```
---
### 方案B:分步提交
**步骤1:提交JS优化**
```bash
git add themes/Ying/layouts/partials/footer.html
git add themes/Ying/assets/js/modules/mypjax.js
git commit -m "perf: JS按需加载优化 + PJAX适配"
```
**步骤2:提交字体优化**
```bash
git add themes/Ying/assets/css/main.css
git add themes/Ying/static/font/zql-v2-subset.*
git add themes/Ying/static/font/used_chars.txt
git commit -m "perf: 字体子集化优化 - 减少37%"
```
**步骤3:提交自动化工作流**
```bash
git add .github/workflows/subset-fonts.yml
git add requirements.txt
git commit -m "ci: 添加字体子集化自动化工作流"
```
**步骤4:推送所有提交**
```bash
git push origin main
```
---
## 📋 提交前检查清单
### 文件检查
- [ ] main.css已修改(字体路径)
- [ ] zql-v2-subset.woff2已生成(757KB)
- [ ] zql-v2-subset.woff已生成(757KB)
- [ ] subset-fonts.yml已创建
- [ ] requirements.txt已创建
### 功能检查
- [ ] 首页正常显示
- [ ] 文章详情页正常
- [ ] 评论区正常加载
- [ ] 深色模式正常
- [ ] 字体显示正常
### 性能检查
- [ ] Network面板显示字体大小 ~757KB
- [ ] JS大小减少(首页~350KB)
- [ ] 无Console错误
---
## 🔍 验证提交
### 提交后检查
```bash
# 查看提交历史
git log --oneline -5
# 查看提交详情
git show HEAD
# 查看远程是否同步
git fetch origin
git log --oneline origin/main -3
```
### GitHub Actions验证
1. 访问:`https://github.com/zqlit/blog/actions`
2. 查看是否有新的工作流运行
3. 检查工作流状态
---
## 💡 提交最佳实践
### 1. 清晰的提交信息
**好的示例:**
```
perf: JS按需加载优化 - 首页减少56%
- 拆分为core.js、page-only.js、deferred.js
- 核心JS始终加载
- 页面特定JS按需加载
- 完美适配PJAX导航
```
**不好的示例:**
```
update
fix
perf
```
---
### 2. 原子性提交
**好的做法:**
- 一个提交解决一个问题
- 便于回滚和追踪
- 代码审查更容易
**不好的做法:**
- 一个提交包含多个不相关修改
- 难以回滚
- 代码审查困难
---
### 3. 测试后再提交
**流程:**
1. 本地测试通过
2. 提交代码
3. 推送到远程
4. 等待CI/CD运行
5. 验证部署成功
---
## 🔄 回滚方案
### 如果提交后发现问题
**回滚到上一个提交:**
```bash
# 查看提交历史
git log --oneline -10
# 回滚到特定提交
git revert <commit-hash>
# 或者回滚到上一个提交
git reset --hard HEAD~1
git push origin main --force
```
**注意:** `--force` 会覆盖远程历史,谨慎使用
---
## 📊 提交统计
### 本次优化提交
**提交次数:** 3-4次
**修改文件:** 8-10个
**新增文件:** 5-6个
**代码行数:** +500行(估算)
### 提交时间线
```
Day 1: JS优化
├── 修改footer.html
├── 修改mypjax.js
└── 测试验证
Day 2: 字体优化
├── 运行子集化脚本
├── 修改main.css
└── 测试验证
Day 3: 自动化
├── 创建subset-fonts.yml
├── 创建requirements.txt
└── 测试自动化
```
---
## ✅ 提交完成
### 验证成功
- ✅ 所有文件已提交
- ✅ GitHub Actions正常运行
- ✅ 部署成功
- ✅ 性能提升生效
### 后续使用
**现在可以:**
- ✅ 发布新文章时自动优化
- ✅ 享受性能提升
- ✅ 无需手动干预
---
**提交指南完成!** 🎉
**祝你提交顺利!**
@@ -0,0 +1,190 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 11-JS优化测试指南
**创建日期:** 2026-06-03
**版本:** v1.0
**适用范围:** JS按需加载优化测试
---
## 🧪 测试步骤
### 步骤1:构建并启动Hugo
```bash
cd E:\GitHub\blog
hugo --cleanDestinationDir
hugo server -D
```
访问:`http://localhost:1313`
---
### 步骤2:Network面板检查
1. 打开DevTools(F12)
2. 切换到 **Network** 面板
3. 刷新页面(Ctrl+Shift+R)
**预期结果:**
- ✅ 看到 `core.js` 文件加载(~200KB)
- ✅ 首页不加载 `page-only.js`
- ✅ 文章详情页加载 `page-only.js`(~180KB)
- ✅ 总体JS大小显著减少
---
### 步骤3:Lighthouse测试
1. 切换到 **Lighthouse** 面板
2. 选择 **Performance**
3. 点击 **Analyze page load**
**预期指标:**
- [ ] Performance得分:75-85(提升15-25分)
- [ ] TTI:改善20-30%
- [ ] TBT:改善40-50%
- [ ] Speed Index:改善20-30%
---
## 🔍 功能测试清单
### 首页功能
**核心功能(必须正常):**
- [ ] 导航菜单点击正常
- [ ] 搜索框打开/关闭正常
- [ ] 搜索结果显示正常
- [ ] 主题切换(深色/浅色)正常
- [ ] 文章列表显示正常
- [ ] 分页功能正常
- [ ] 浮动工具栏正常
**条件加载功能:**
- [ ] 无限滚动正常(如果启用)
---
### 文章详情页功能
**核心功能(必须正常):**
- [ ] 文章内容正常显示
- [ ] 图片灯箱正常(点击查看大图)
- [ ] 返回顶部按钮正常
**按需加载功能(必须正常):**
- [ ] Artalk评论区正常加载(等待1-2秒)
- [ ] 评论功能正常(发布、回复)
- [ ] 打赏按钮功能正常
- [ ] 段落评论正常(如果启用)
---
### 其他页面功能
- [ ] 友链页面(/links)正常
- [ ] circles页面(/circles)正常
- [ ] 归档页面(/archives)正常
- [ ] 搜索结果页正常
---
### 跨页面功能
- [ ] PJAX导航正常(页面无刷新切换)
- [ ] 浏览器前进/后退正常
- [ ] 书签/分享链接正常
---
## 📊 性能指标对比表
### Network面板数据
| 资源 | 优化前大小 | 优化后大小 | 减少 |
|------|-----------|-----------|------|
| **首页JS** | 800KB | ____KB | ____% |
| **文章页JS** | 800KB | ____KB | ____% |
| **总体资源** | ~3MB | ____KB | ____% |
### Lighthouse指标
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **Performance得分** | 60 | ____ | +____ |
| **FCP** | 2.5s | ____s | ____% |
| **LCP** | 3.5s | ____s | ____% |
| **TTI** | 4.0s | ____s | ____% |
| **TBT** | 350ms | ____ms | ____% |
| **Speed Index** | 3.0s | ____s | ____% |
---
## 🐛 故障排除
### 问题1:评论区未加载
**解决方案:**
1. 打开Console查看错误
2. 检查Network面板,确认page-only.js加载成功
3. 等待2-3秒,Artalk可能需要时间初始化
---
### 问题2:功能延迟响应
**解决方案:**
- 这是预期行为,用户可能会感觉到轻微延迟
- 如果延迟明显(>3秒),考虑将该模块移到core.js
---
### 问题3:无限滚动失效
**解决方案:**
1. 检查hugo.toml中infiniteScroll.enable是否为true
2. 查看Console是否有错误
3. 确认infinite-scroll.js加载成功
---
## 🔄 回滚方案
### 如果优化后出现严重问题
```bash
# 备份当前文件
cp themes/Ying/layouts/partials/footer.html themes/Ying/layouts/partials/footer.html.optimized
# 恢复原始代码(参考TEST_JS_OPTIMIZATION.md中的回滚方案)
```
---
## ✅ 测试通过标准
### 功能标准(必须全部通过)
- ✅ 所有页面正常显示
- ✅ 核心功能正常(导航、搜索、主题切换)
- ✅ 文章详情页功能正常(评论、打赏、灯箱)
- ✅ 无限滚动正常(如果启用)
- ✅ PJAX导航正常
- ✅ 无Console错误(或只有非关键警告)
### 性能标准(至少达到一项)
- ✅ Lighthouse Performance得分提升10+分
- ✅ TTI改善15%+
- ✅ TBT改善30%+
- ✅ 总体JS大小减少40%+
---
**JS优化测试指南完成!** 🎉
**测试通过后即可提交代码!**
@@ -0,0 +1,197 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 12-字体优化测试指南
**创建日期:** 2026-06-03
**版本:** v1.0
**适用范围:** 字体子集化优化测试
---
## 🧪 测试步骤
### 步骤1:重新构建Hugo
```bash
cd E:\GitHub\blog
hugo --cleanDestinationDir
hugo server -D
```
访问:`http://localhost:1313`
---
### 步骤2:检查字体加载
1. 打开DevTools(F12)
2. 切换到 **Network** 面板
3. 刷新页面
4. 筛选 `woff2` 或 `woff`
**预期结果:**
- ✅ 看到 `zql-v2-subset.woff2` 被加载
- ✅ 文件大小:~757KB(而不是1.2MB)
- ✅ 无404错误
---
### 步骤3:视觉检查
#### 中文字符测试
- [ ] 导航菜单中文正常
- [ ] 文章标题中文正常
- [ ] 文章内容中文正常
- [ ] 深色模式下中文正常
#### 英文字符测试
- [ ] 英文字母正常(A-Z, a-z)
- [ ] 数字正常(0-9)
- [ ] 常用符号正常(@#$%)
#### 标点符号测试
- [ ] 中文标点正常(,。!?、;:""'')
- [ ] 英文标点正常(,.!?;:'")
- [ ] 括号正常(()【】《》)
#### 不同页面测试
- [ ] 首页字体正常
- [ ] 文章详情页字体正常
- [ ] 友链页面字体正常
- [ ] 归档页面字体正常
- [ ] 移动端字体正常
---
### 步骤4:深色模式测试
1. 点击头像或主题切换按钮
2. 检查深色模式下:
- [ ] 所有文字正常显示
- [ ] 字体颜色正确
- [ ] 无闪烁或异常
---
## 📊 性能验证
### Network面板数据记录
| 文件 | 优化前 | 优化后 | 减少 |
|------|--------|--------|------|
| **zql-v2.woff2** | 1.2MB | ____KB | ____% |
| **zql-v2.woff** | 1.2MB | ____KB | ____% |
| **总字体大小** | 2.4MB | ____KB | ____% |
---
### Lighthouse测试(可选)
使用Chrome DevTools的Lighthouse面板测试
**预期指标:**
- Performance得分:75-85
- 无字体相关警告
- FCP:改善20-30%
---
## 🔍 验证子集化效果
### 检查字符覆盖
你的子集字体包含 **2,485个字符**,包括:
**基本字符:**
- ✅ 英文字母(A-Z, a-z)
- ✅ 数字(0-9)
- ✅ 常用标点符号
**中文字符:**
- ✅ 常用汉字(根据网站内容提取)
- ✅ 中文标点符号
- ✅ CJK符号
---
## 🐛 故障排除
### 问题1:字符显示为方块(□)
**解决方案A:重新运行子集化**
```bash
python scripts/subset-font-safe.py
```
**解决方案B:保留原始字体作为fallback**
```css
@font-face {
font-family: 'zql';
src: url('../font/zql-v2-subset.woff2') format('woff2');
font-display: swap;
}
@font-face {
font-family: 'zql-full';
src: url('../font/zql-v2.woff2') format('woff2');
font-display: swap;
}
body {
font-family: 'zql', 'zql-full', serif;
}
```
---
### 问题2:字体文件404错误
**解决方案:**
1. 确认文件存在:`ls themes/Ying/static/font/zql-v2-subset.*`
2. 检查CSS路径是否正确
3. 清理Hugo缓存:`hugo --cleanDestinationDir`
---
## 🔄 回滚方案
### 如果优化后出现问题
```bash
# 恢复原始字体
cp themes/Ying/static/font/zql-v2.woff2.backup themes/Ying/static/font/zql-v2.woff2
cp themes/Ying/static/font/zql-v2.woff.backup themes/Ying/static/font/zql-v2.woff
# 恢复CSS
git checkout themes/Ying/assets/css/main.css
```
---
## ✅ 测试通过标准
### 功能标准(必须全部通过)
- ✅ 所有页面正常显示
- ✅ 中文字符正常(常用汉字、标点)
- ✅ 英文字符正常(字母、数字、符号)
- ✅ 深色模式正常
- ✅ 响应式布局正常
- ✅ 无Console错误
### 性能标准(至少达到一项)
- ✅ 字体大小减少30%+(1.2MB → 757KB ✅ 已达成)
- ✅ 加载时间减少20%+
- ✅ Lighthouse无字体警告
---
**字体优化测试指南完成!** 🎉
**测试通过后即可提交代码!**
@@ -0,0 +1,239 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 13-主题全面优化分析
**创建日期:** 2026-06-03
**版本:** v1.0
**分析对象:** Hugo主题Ying
**分析范围:** 性能、代码质量、SEO、安全性
---
## 📋 主题概述
### 主题信息
- **主题名称:** Ying
- **主题类型:** Hugo静态站点主题
- **功能特点:** 简洁优雅、深色模式、PJAX导航、响应式设计
### 技术栈
- **前端框架:** UIkit
- **图标库:** Remixicon
- **评论系统:** Artalk
- **图表库:** Echarts
- **导航技术:** PJAX
---
## ✅ 优点
### 1. 优秀的资源压缩策略
- ✅ CSS/JS文件合并和压缩
- ✅ 使用Hugo资源管道
- ✅ Fingerprint缓存破坏
### 2. 深色模式支持
- ✅ CSS变量实现
- ✅ 平滑过渡动画
- ✅ 用户偏好存储
### 3. 响应式设计
- ✅ 移动端适配
- ✅ 触摸优化
- ✅ 灵活布局
### 4. 模块化JavaScript
- ✅ 功能模块分离
- ✅ 按需初始化
- ✅ 错误处理
### 5. 现代化SEO基础
- ✅ Open Graph标签
- ✅ Twitter Card
- ✅ Canonical URL
---
## ⚠️ 需要优化的地方
### 1. 性能优化(已实施)✅
**问题:**
- 所有JS打包为单个bundle(800KB)
- 字体文件过大(1.2MB)
- 所有页面加载所有资源
**解决方案:**
- ✅ JS按需加载(减少56%)
- ✅ 字体子集化(减少37%)
- ✅ 总体性能提升50%
---
### 2. CSS架构(建议优化)
**问题:**
- main.css为单一大文件(500KB+)
- 深度嵌套选择器
- 缺少CSS变量管理
**建议:**
- 拆分为模块化CSS
- 使用BEM命名规范
- 集中管理CSS变量
---
### 3. SEO完善(建议优化)
**问题:**
- 缺少结构化数据(JSON-LD)
- Meta keywords逻辑不完善
- 缺少面包屑导航
**建议:**
- 添加JSON-LD结构化数据
- 优化meta keywords提取
- 添加面包屑导航
---
### 4. 安全性(建议优化)
**问题:**
- 缺少SRI(Subresource Integrity)
- 未实施CSP(Content Security Policy)
- 第三方脚本安全审计
**建议:**
- 添加SRI哈希
- 实施CSP头部
- 审计第三方依赖
---
### 5. 可访问性(建议优化)
**问题:**
- 缺少ARIA标签
- 键盘导航不完整
- 颜色对比度可能不足
**建议:**
- 添加ARIA标签
- 完善键盘导航
- 检查颜色对比度
---
## 📊 优化优先级
### 高优先级(已实施)✅
1. **JS按需加载** - 减少56%
2. **字体子集化** - 减少37%
3. **GitHub Actions自动化** - 100%自动化
---
### 中优先级(建议实施)
1. **CSS架构重构** - 提升可维护性
2. **SEO完善** - 提升搜索引擎排名
3. **安全性增强** - 保护用户安全
---
### 低优先级(可选)
1. **可访问性改进** - 提升用户体验
2. **代码注释增强** - 提升可读性
3. **文档完善** - 便于维护
---
## 🎯 已实施的优化
### 1. JS按需加载优化
**优化效果:**
- 首页JS:800KB → 350KB(⚡ -56%)
- 文章页JS:800KB → 450KB(⚡ -44%)
**技术实现:**
- 代码拆分为4个bundle
- 核心JS始终加载
- 页面特定JS按需加载
- 非关键JS延迟加载
---
### 2. 字体子集化优化
**优化效果:**
- 字体大小:1.2MB → 757KB(⚡ -37%)
**技术实现:**
- Python fonttools提取字符
- 生成子集字体(2,485字符)
- 更新CSS字体声明
---
### 3. GitHub Actions自动化
**自动化程度:** 100%
**功能:**
- 内容更新时自动优化字体
- 每周定期检查
- 智能检测变更
- 自动部署到UpYun
---
## 📈 优化效果总结
### 性能提升
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **总体资源** | ~3MB | ~1.5MB | ⚡ -50% |
| **加载速度** | 慢 | 快 | ⚡ +40% |
| **Lighthouse** | 60 | 75-80 | ⚡ +33% |
---
## 💡 后续建议
### 1. 定期监控
- ✅ 每月Lighthouse测试
- ✅ 监控Core Web Vitals
- ✅ 收集用户反馈
### 2. 持续优化
- ✅ CSS架构重构
- ✅ SEO完善
- ✅ 安全性增强
### 3. 文档维护
- ✅ 更新优化文档
- ✅ 记录最佳实践
- ✅ 分享优化经验
---
**主题全面优化分析完成!** 🎉
**核心优化已实施,性能提升50%!**
@@ -0,0 +1,201 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 14-JS优化最终方案
**创建日期:** 2026-06-03
**版本:** v2.0
**状态:** ✅ 已实施
**优化效果:** 首页JS减少56%,文章页JS减少44%
---
## 📋 方案概述
### 优化目标
将JS拆分为多个bundle,实现按需加载,提升性能。
### 优化效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **首页JS** | 800KB | 350KB | ⚡ -56% |
| **文章页JS** | 800KB | 450KB | ⚡ -44% |
| **TTI** | 4.0s | ~2.0s | ⚡ -50% |
| **TBT** | 350ms | ~100ms | ⚡ -71% |
---
## 🔧 技术实现
### JS Bundle拆分
**1. core.js (200KB) - 始终加载**
- UIkit框架
- 图标字体
- 图片灯箱
- 工具函数
- 搜索功能
- 浮动工具
- 进度条
- PJAX
- 主题主逻辑
**2. page-only.js (180KB) - 文章详情页**
- Artalk评论系统
- 段落评论
- 打赏功能
**3. deferred.js (25KB) - 延迟加载**
- Toast消息
- 图片懒加载
**4. infinite-scroll.js (20KB) - 首页(如果启用)**
**5. tiaozhuan.js (8KB) - 特定页面**
---
## 🛠️ 实施步骤
### 步骤1:修改footer.html
**文件:** `themes/Ying/layouts/partials/footer.html`
**修改内容:**
- 创建核心JS bundle
- 创建页面特定JS bundle
- 创建延迟加载JS bundle
- 首页专用JS
- 特定页面专用JS
---
### 步骤2:适配PJAX
**文件:** `themes/Ying/assets/js/modules/mypjax.js`
**修改内容:**
- 在pjax:complete事件中添加动态加载逻辑
- 检测当前页面是否为文章详情页
- 动态加载page-only.js
- 初始化Artalk等功能
---
## 📊 加载时序
### 优化前
```
页面加载
↓
下载bundle.js (800KB)
↓
执行所有JS
↓
渲染页面
↓
用户可以交互
```
---
### 优化后
```
页面加载
↓
下载core.js (200KB) - 立即
↓
渲染页面(核心功能可用)
↓
用户可以交互
↓
下载page-only.js (180KB) - 按需(仅文章页)
↓
下载deferred.js (25KB) - 延迟(浏览器空闲)
↓
所有功能可用
```
---
## 🧪 测试验证
### 功能测试
- [ ] 首页功能正常
- [ ] 文章详情页正常
- [ ] 评论区正常加载
- [ ] 打赏功能正常
- [ ] PJAX导航正常
- [ ] 无限滚动正常(如果启用)
### 性能测试
- [ ] Network面板显示JS大小减少
- [ ] Lighthouse得分提升
- [ ] 无Console错误
---
## 🔄 回滚方案
### 如果优化后出现严重问题
```bash
# 恢复footer.html
git checkout themes/Ying/layouts/partials/footer.html
# 恢复mypjax.js
git checkout themes/Ying/assets/js/modules/mypjax.js
# 重新构建
hugo --cleanDestinationDir
```
---
## 💡 最佳实践
### 1. 逐步优化
- ✅ 先测试核心功能
- ✅ 逐步添加按需加载
- ✅ 充分测试每个步骤
- ✅ 记录问题和解决方案
### 2. 监控性能
- ✅ 定期Lighthouse测试
- ✅ 监控网络请求
- ✅ 检查Console错误
- ✅ 记录性能数据
---
## 📈 优化效果总结
### 性能提升
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **首页JS** | 800KB | 350KB | ⚡ -56% |
| **文章页JS** | 800KB | 450KB | ⚡ -44% |
| **TTI** | 4.0s | ~2.0s | ⚡ -50% |
| **TBT** | 350ms | ~100ms | ⚡ -71% |
### 用户体验提升
- 🚀 **首屏更快** - 资源减少56%
- ⚡ **交互更流畅** - TTI提升50%
- 📱 **移动端更好** - 节省带宽
- 🎨 **功能完整** - 所有功能正常
---
**JS优化最终方案完成!** 🎉
**优化效果显著,性能提升56%!**
@@ -0,0 +1,207 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 15-字体优化手动指南
**创建日期:** 2026-06-03
**版本:** v1.0
**适用场景:** 手动执行字体子集化
---
## 📋 手动操作步骤
### 步骤1:备份原始字体(2分钟)
**Windows:**
```bash
cd E:\GitHub\blog
copy themes\Ying\static\font\zql-v2.woff2 themes\Ying\static\font\zql-v2.woff2.backup
copy themes\Ying\static\font\zql-v2.woff themes\Ying\static\font\zql-v2.woff.backup
```
**Mac/Linux:**
```bash
cd E:\GitHub\blog
cp themes/Ying/static/font/zql-v2.woff2 themes/Ying/static/font/zql-v2.woff2.backup
cp themes/Ying/static/font/zql-v2.woff themes/Ying/static/font/zql-v2.woff.backup
```
---
### 步骤2:安装Python依赖(3分钟)
```bash
pip install fonttools brotli
```
**验证安装:**
```bash
python -c "from fontTools.ttLib import TTFont; print('fonttools installed')"
```
---
### 步骤3:构建Hugo站点(2分钟)
```bash
cd E:\GitHub\blog
hugo --destination=public
```
**验证构建:**
```bash
ls public/
```
---
### 步骤4:运行字体子集化(5分钟)
```bash
python scripts/subset-font-safe.py
```
**预期输出:**
```
🔤 字体子集化工具(安全版本)
==================================================
✅ 找到public目录,将扫描构建后的HTML
🔍 扫描目录: content, layouts, public
📝 提取了 2492 个唯一字符
💾 字符列表已保存到: themes/Ying/static/font\used_chars.txt
✂️ 正在生成子集字体...
✅ 子集化完成!
📊 优化结果:
子集字符数: 2485
子集文件大小: 739.7 KB
减少: 486.8 KB (39.7%)
🎉 所有子集字体生成成功!
```
---
### 步骤5:验证生成的文件(1分钟)
**Windows:**
```bash
dir themes\Ying\static\font\zql-v2-subset.*
```
**Mac/Linux:**
```bash
ls -lh themes/Ying/static/font/zql-v2-subset.*
```
**预期大小:**
- `zql-v2-subset.woff2`: ~757KB
- `zql-v2-subset.woff`: ~757KB
---
### 步骤6:更新CSS字体声明(3分钟)
**编辑文件:** `themes/Ying/assets/css/main.css`
**找到第1-14行的字体声明:**
```css
@font-face {
font-family: 'zql';
src: url('../font/zql-v2.woff2') format('woff2'),
url('../font/zql-v2.woff') format('woff');
font-display: swap;
unicode-range: U+0000-007F,
U+4E00-9FFF,
U+2000-206F,
U+3000-303F;
}
```
**替换为:**
```css
@font-face {
font-family: 'zql';
src: url('../font/zql-v2-subset.woff2') format('woff2'),
url('../font/zql-v2-subset.woff') format('woff');
font-display: swap;
}
```
---
### 步骤7:测试验证(5分钟)
**重新构建:**
```bash
hugo --cleanDestinationDir
hugo server -D
```
**访问:** `http://localhost:1313`
**检查清单:**
- [ ] 中文字符正常
- [ ] 英文字符正常
- [ ] 深色模式正常
- [ ] Network面板显示字体大小 ~757KB
---
### 步骤8:提交代码(2分钟)
```bash
git add themes/Ying/assets/css/main.css
git add themes/Ying/static/font/zql-v2-subset.*
git commit -m "perf: 字体子集化优化 - 减少37%"
git push origin main
```
---
## 🐛 故障排除
### 问题1:字符显示为方块
**解决方案:** 重新运行子集化脚本
### 问题2:字体文件404
**解决方案:** 检查CSS路径,清理Hugo缓存
### 问题3:Python脚本运行失败
**解决方案:** 检查Python版本和依赖
---
## 🔄 回滚方案
```bash
# 恢复原始字体
cp themes/Ying/static/font/zql-v2.woff2.backup themes/Ying/static/font/zql-v2.woff2
cp themes/Ying/static/font/zql-v2.woff.backup themes/Ying/static/font/zql-v2.woff
# 恢复CSS
git checkout themes/Ying/assets/css/main.css
```
---
## 📊 操作时间
- 步骤1:备份字体 - 2分钟
- 步骤2:安装依赖 - 3分钟
- 步骤3:构建Hugo - 2分钟
- 步骤4:运行子集化 - 5分钟
- 步骤5:验证文件 - 1分钟
- 步骤6:更新CSS - 3分钟
- 步骤7:测试验证 - 5分钟
- 步骤8:提交代码 - 2分钟
**总计:约23分钟**
---
**字体优化手动指南完成!** 🎉
**按照步骤操作即可完成字体优化!**
@@ -0,0 +1,316 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 16-实施总结报告
**创建日期:** 2026-06-03
**版本:** v1.0
**报告类型:** 性能优化实施总结
**项目状态:** ✅ 已完成
---
## 📋 项目概述
### 项目目标
对Hugo主题Ying进行全面性能优化,包括:
1. JS按需加载优化
2. 字体子集化优化
3. GitHub Actions自动化
### 优化效果
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **总体资源** | ~3MB | ~1.5MB | ⚡ -50% |
| **加载速度** | 慢 | 快 | ⚡ +40% |
| **Lighthouse** | 60 | 75-80 | ⚡ +33% |
---
## ✅ 已完成的工作
### 1. JS按需加载优化(第1步)
**完成时间:** 2026-06-03
**实施耗时:** 约2小时
**优化效果:**
- 首页JS:800KB → 350KB(⚡ -56%)
- 文章页JS:800KB → 450KB(⚡ -44%)
**主要工作:**
- ✅ JS代码拆分为4个bundle
- ✅ 核心JS始终加载(~200KB)
- ✅ 页面特定JS按需加载(~180KB)
- ✅ 非关键JS延迟加载(~25KB)
- ✅ PJAX完美适配
**修改文件:**
- `themes/Ying/layouts/partials/footer.html`
- `themes/Ying/assets/js/modules/mypjax.js`
---
### 2. 字体子集化优化(第2步)
**完成时间:** 2026-06-03
**实施耗时:** 约1.5小时
**优化效果:**
- 字体大小:1.2MB → 757KB(⚡ -37%)
**主要工作:**
- ✅ 使用Python fonttools提取字符
- ✅ 生成子集字体(2,485个字符)
- ✅ 更新CSS字体声明
- ✅ 保持所有字符正常显示
**修改文件:**
- `themes/Ying/assets/css/main.css`
- `themes/Ying/static/font/zql-v2-subset.woff2`
- `themes/Ying/static/font/zql-v2-subset.woff`
- `themes/Ying/static/font/used_chars.txt`
---
### 3. GitHub Actions自动化(第3步)
**完成时间:** 2026-06-03
**实施耗时:** 约1小时
**自动化程度:** 100%
**主要工作:**
- ✅ 创建字体子集化工作流
- ✅ 配置自动触发条件
- ✅ 与deploy.yml完美协调
- ✅ 智能检测变更
- ✅ 自动部署到UpYun
**创建文件:**
- `.github/workflows/subset-fonts.yml`
- `requirements.txt`
---
## 📊 技术实现
### JS优化策略
```
core.js (200KB) - 始终加载
├── UIkit
├── 图标字体
├── 图片灯箱
├── 工具函数
├── 搜索功能
├── 浮动工具
├── 进度条
├── PJAX
└── 主题主逻辑
page-only.js (180KB) - 文章详情页
├── Artalk评论
├── 段落评论
└── 打赏功能
deferred.js (25KB) - 延迟加载
├── Toast消息
└── 图片懒加载
infinite-scroll.js (20KB) - 首页(如果启用)
tiaozhuan.js (8KB) - 特定页面
```
---
### 字体优化策略
```
原始字体(1.2MB)
├── 20,000+ 字符
└── 完整中文字符集
子集字体(757KB)
├── 2,485 个字符
├── 常用中文字符
├── 英文字母和数字
├── 常用标点符号
└── 特殊符号
优化效果:-37%
```
---
### 自动化流程
```
用户push内容更新
↓
deploy.yml(部署文章)
↓
subset-fonts.yml(优化字体)
↓
deploy.yml(部署新字体)
↓
✅ 完成!
```
---
## 🎯 项目亮点
### 1. 性能显著提升
- ✅ 资源减少50%
- ✅ 加载速度提升40%
- ✅ Lighthouse 75-80分
- ✅ 用户体验大幅改善
### 2. 完全自动化
- ✅ GitHub Actions自动运行
- ✅ 智能检测变更
- ✅ 无需手动干预
- ✅ 节省时间和精力
### 3. 智能优化
- ✅ 只在需要时优化
- ✅ 避免不必要的部署
- ✅ 节省资源和成本
- ✅ 保持系统高效
### 4. 完整文档
- ✅ 16份详细文档
- ✅ 覆盖所有场景
- ✅ 故障排除指南
- ✅ 最佳实践说明
---
## 📁 文档清单
### 核心文档(3份)
- ✅ 01-项目完成总结.md
- ✅ 02-方案1完成总结.md
- ✅ 03-三步优化完整指南.md
### 优化实施(4份)
- ✅ 04-JS按需加载优化.md
- ✅ 05-PJAX适配说明.md
- ✅ 06-PJAX修复总结.md
- ✅ 07-字体子集化优化.md
### 自动化(3份)
- ✅ 08-GitHub-Actions使用指南.md
- ✅ 09-Actions修复指南.md
- ✅ 10-提交指南.md
### 测试验证(2份)
- ✅ 11-JS优化测试指南.md
- ✅ 12-字体优化测试指南.md
### 详细方案(4份)
- ✅ 13-主题全面优化分析.md
- ✅ 14-JS优化最终方案.md
- ✅ 15-字体优化手动指南.md
- ✅ 16-实施总结报告.md(本文件)
---
## 💡 后续使用
### 日常开发
```bash
# 发布新文章
git add content/posts/new-article.md
git commit -m "feat: new article"
git push origin main
# 等待自动化(3-10分钟)
# - deploy.yml:部署文章
# - subset-fonts.yml:优化字体(如果需要)
# - deploy.yml:部署新字体(如果需要)
```
### 监控系统
```bash
# 查看GitHub Actions
https://github.com/zqlit/blog/actions
# 查看工作流状态
- Deploy to Production(部署)
- Font Subset Optimization(字体优化)
```
### 性能测试
```bash
# 每月测试一次
# 使用Chrome DevTools的Lighthouse
# 或者:https://pagespeed.web.dev/
# 记录:Performance、FCP、LCP、TTI
```
---
## 🎓 学到了什么?
### 技术技能
- ✅ JavaScript代码拆分
- ✅ 字体子集化技术
- ✅ GitHub Actions工作流
- ✅ Hugo静态站点优化
### DevOps实践
- ✅ CI/CD流程设计
- ✅ 自动化部署
- ✅ 工作流协调
- ✅ 性能监控
---
## 🏆 成就解锁
- ⚡ **性能优化大师** - 资源减少50%
- 🤖 **自动化专家** - 完整CI/CD流程
- 🚀 **前端优化师** - Lighthouse 75-80分
- 💡 **DevOps工程师** - GitHub Actions精通
---
## 🎊 项目完成
**恭喜你完成了完整的Hugo博客性能优化和自动化系统!**
- ✅ 性能提升50%
- ✅ 加载速度提升40%
- ✅ Lighthouse 75-80分
- ✅ 完全自动化
- ✅ 生产就绪
**现在可以专注于创作优质内容了!** 🚀
---
**项目完成时间:** 2026-06-03
**总耗时:** 约4.5小时
**优化效果:** 性能提升50%
**自动化程度:** 100%
**维护成本:** 0(完全自动化)
**祝你博客越办越好!** 🎉
---
**实施总结报告完成!** 🎉
**所有16份文档已创建!**
@@ -0,0 +1,405 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# Ying主题 Markdown 表格样式使用指南
**创建日期:** 2026-06-03
**版本:** v2.0(重新设计)
**状态:** ✅ 已实现
---
## 📋 表格样式概述
### 设计理念
**完全符合Ying主题风格:**
- ✅ **圆角12px** - 与blockquote一致
- ✅ **毛玻璃效果** - 与.main容器一致
- ✅ **柔和阴影** - 层次丰富不突兀
- ✅ **透明边框** - rgba颜色更自然
- ✅ **深色模式** - 柔和的rgba过渡
- ✅ **斑马条纹** - 提升可读性
- ✅ **悬停效果** - 交互友好
- ✅ **响应式设计** - 移动端自动横向滚动
- ✅ **多种变体** - 紧凑型、无边框、高亮
### 设计参考
**参考元素:**
- blockquote(圆角、阴影、边框风格)
- .main容器(毛玻璃效果)
- 代码块(背景、边框风格)
---
## 🎨 浅色模式设计
### 配色方案
| 元素 | 颜色 | 说明 |
|------|------|------|
| **表格背景** | `rgba(255, 255, 255, 0.95)` | 毛玻璃效果 |
| **表格边框** | `rgba(0, 0, 0, 0.08)` | 透明柔和 |
| **表格阴影** | `0 8px 24px rgba(0, 0, 0, 0.06)` | 层次丰富 |
| **表头背景** | 渐变 `rgba(0,0,0,0.03)` → `rgba(0,0,0,0.01)` | 微妙渐变 |
| **表头文字** | `rgba(30, 30, 30, 0.92)` | 深灰,可读性强 |
| **表头边框** | `rgba(0, 0, 0, 0.1)` | 底部2px |
| **单元格背景** | 白色 / `rgba(0,0,0,0.015)` | 斑马纹 |
| **单元格文字** | `rgba(30, 30, 30, 0.85)` | 中灰 |
| **单元格边框** | `rgba(0, 0, 0, 0.05)` | 浅灰底边 |
| **悬停背景** | `rgba(0, 0, 0, 0.04)` | 柔和高亮 |
| **链接颜色** | 使用主题链接色 | 与正文一致 |
| **代码背景** | `rgba(0, 0, 0, 0.04)` | 浅灰 |
| **代码文字** | `rgba(30, 30, 30, 0.9)` | 深灰 |
### 设计特点
- ✅ **圆角12px** - 与blockquote完全一致
- ✅ **毛玻璃** - `backdrop-filter: blur(8px)`
- ✅ **透明边框** - 使用rgba而不是实色
- ✅ **柔和阴影** - 避免突兀感
---
## 🌙 深色模式设计
### 配色方案
| 元素 | 颜色 | 说明 |
|------|------|------|
| **表格背景** | `rgba(15, 23, 42, 0.95)` | 深蓝毛玻璃 |
| **表格边框** | `rgba(148, 163, 184, 0.18)` | 透明灰边框 |
| **表格阴影** | `0 10px 28px rgba(0, 0, 0, 0.22)` | 更深的阴影 |
| **表头背景** | 渐变 `rgba(30,41,59,0.92)` → `rgba(15,23,42,0.78)` | 深蓝渐变 |
| **表头文字** | `rgba(226, 232, 240, 0.96)` | 浅灰白 |
| **表头边框** | `rgba(148, 163, 184, 0.2)` | 深灰底边 |
| **单元格背景** | 深蓝 / `rgba(30,41,59,0.5)` | 深色斑马纹 |
| **单元格文字** | `rgba(226, 232, 240, 0.9)` | 浅灰 |
| **单元格边框** | `rgba(148, 163, 184, 0.1)` | 深灰底边 |
| **悬停背景** | `rgba(30, 41, 59, 0.8)` | 柔和高亮 |
| **链接颜色** | 使用主题深色链接色 | 与正文一致 |
| **代码背景** | `rgba(148, 163, 184, 0.15)` | 深灰 |
| **代码文字** | `rgba(226, 232, 240, 0.95)` | 浅灰白 |
### 设计特点
- ✅ **柔和过渡** - 使用rgba透明度
- ✅ **避免纯黑** - 深蓝灰更舒适
- ✅ **层次分明** - 半透明边框和阴影
- ✅ **护眼设计** - 减少对比度
---
## 🎯 与主题一致性
### 对比其他元素
| 元素 | 圆角 | 边框 | 阴影 | 背景 |
|------|------|------|------|------|
| **blockquote** | 12px | rgba透明 | 柔和 | 渐变 |
| **.main容器** | 8px | rgba透明 | 层次丰富 | 毛玻璃 |
| **表格** | **12px** | **rgba透明** | **柔和** | **毛玻璃** ✅ |
| **代码块** | 6px | rgba透明 | 无 | 浅灰 |
**表格风格与blockquote高度一致!** ✅
---
## 📱 响应式设计
### 移动端优化(<768px)
**自动处理:**
- ✅ 表格自动横向滚动
- ✅ 第一列固定(sticky定位)
- ✅ 减小内边距(12px → 10px)
- ✅ 减小字体(0.85em)
- ✅ 圆角调整(8px)
- ✅ 触摸友好
**效果:**
- ✅ 在小屏幕完整查看表格
- ✅ 第一列始终可见
- ✅ 流畅滚动体验
- ✅ 性能优化
---
## 🎨 表格变体
### 1. 紧凑型表格(.compact)
**适用场景:** 数据密集型,需要节省空间
**Markdown语法:**
```markdown
| 列1 | 列2 | 列3 | {.compact}
|-----|-----|-----|---------
| 数据 | 数据 | 数据 |
```
**CSS类:** `table.compact`
**效果:**
- ✅ 更小的内边距(10px vs 14px)
- ✅ 更小的字体(0.9em)
- ✅ 保持主题风格
---
### 2. 无边框表格(.borderless)
**适用场景:** 简洁风格,不需要明显边框
**Markdown语法:**
```markdown
| 列1 | 列2 | 列3 | {.borderless}
|-----|-----|-----|-------------
| 数据 | 数据 | 数据 |
```
**CSS类:** `table.borderless`
**效果:**
- ✅ 无外边框
- ✅ 无阴影
- ✅ 透明背景
- ✅ 仅有底部分隔线
- ✅ 更加简洁
---
### 3. 高亮表格(.highlight)
**适用场景:** 需要突出的重要表格
**Markdown语法:**
```markdown
| 列1 | 列2 | 列3 | {.highlight}
|-----|-----|-----|------------
| 数据 | 数据 | 数据 |
```
**CSS类:** `table.highlight`
**效果:**
- ✅ 左侧4px边框(浅色:黑色60%透明,深色:灰色70%透明)
- ✅ 更明显的表头背景
- ✅ 更突出的表头边框
- ✅ 引起注意
---
## 📝 使用示例
### 示例1:基础表格
```markdown
| 姓名 | 年龄 | 职业 |
|------|------|------|
| 张三 | 25 | 工程师 |
| 李四 | 30 | 设计师 |
| 王五 | 28 | 产品经理 |
```
**效果:**
- ✅ 圆角12px卡片
- ✅ 毛玻璃背景
- ✅ 斑马条纹
- ✅ 悬停高亮
---
### 示例2:包含代码和链接
```markdown
| 工具 | 版本 | 用途 |
|------|------|------|
| Node.js | `18.0+` | JavaScript运行环境 |
| [Hugo](https://gohugo.io) | `0.128.2` | 静态站点生成器 |
| Git | `2.30+` | 版本控制 |
```
**效果:**
- ✅ 代码块样式(圆角6px,浅灰背景)
- ✅ 链接样式(虚线下划线,悬停变实线)
- ✅ 与主题链接风格一致
---
### 示例3:古风官职表格
```markdown
| 品级 | 官职 | 俸禄 | 说明 |
|------|------|------|------|
| 正一品 | 太师 | 2000 | 最高官职 |
| 从一品 | 太尉 | 1400 | 军事长官 |
| 正二品 | 参知政事 | 1050 | 副宰相 |
| 从二品 | 节度使 | 800 | 地方军政长官 |
| 正三品 | 御史中丞 | 600 | 监察官员 |
| 从三品 | 秘书监 | 480 | 文书管理 |
| 正四品 | 谏议大夫 | 380 | 谏官 |
| 从四品 | 侍读学士 | 300 | 皇帝顾问 |
| 正五品 | 给事中 | 240 | 审查奏章 |
| 从五品 | 知州 | 190 | 地方长官 |
| 正六品 | 侍御史 | 150 | 监察官员 |
| 从六品 | 通判 | 115 | 副地方长官 |
| 正七品 | 知县 | 85 | 县级长官 |
| 从七品 | 殿中侍御史 | 60 | 宫廷监察 |
| 正八品 | 大理评事 | 40 | 司法官员 |
| 从八品 | 录事参军 | 25 | 文书官员 |
| 正九品 | 主簿 | 15 | 县级文书 |
| 从九品 | 司户参军 | 5 | 户籍管理 |
| — | 庶民 | 0 | 普通百姓 |
```
**效果:**
- ✅ 20行数据清晰展示
- ✅ 斑马纹便于阅读
- ✅ 深色模式完美适配
- ✅ 与古风主题协调
---
## 💡 使用技巧
### 1. 对齐方式
```markdown
| 左对齐 | 居中对齐 | 右对齐 |
|:-------|:--------:|-------:|
| 数据 | 数据 | 数据 |
```
**效果:**
- ✅ `:---` 左对齐(默认)
- ✅ `:---:` 居中对齐
- ✅ `---:` 右对齐
---
### 2. 表格内换行
```markdown
| 列1 | 列2 |
|-----|-----|
| 第一行<br>第二行 | 数据 |
```
**效果:**
- ✅ 使用 `<br>` 标签换行
- ✅ 在单元格内显示多行
---
### 3. 代码和链接混合
```markdown
| 工具 | 版本 | 链接 |
|------|------|------|
| Hugo | `0.128.2` | [官网](https://gohugo.io) |
```
**效果:**
- ✅ 代码块特殊样式
- ✅ 链接虚线下划线
- ✅ 悬停效果
---
## 🎨 自定义样式
### 修改圆角
```css
.post-content table {
border-radius: 16px; /* 更大的圆角 */
}
```
### 修改毛玻璃强度
```css
.post-content table {
backdrop-filter: blur(12px); /* 更强的模糊 */
}
```
### 修改阴影
```css
.post-content table {
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.08); /* 更大的阴影 */
}
```
### 修改深色模式透明度
```css
[data-theme="dark"] .post-content table {
background: rgba(15, 23, 42, 0.98); /* 更不透明 */
}
```
---
## ✅ 功能清单
### 基础功能
- [x] 表头样式(微妙渐变)
- [x] 斑马条纹
- [x] 悬停效果
- [x] 圆角12px(与blockquote一致)
- [x] 毛玻璃效果
- [x] 深色模式
### 进阶功能
- [x] 响应式设计
- [x] 移动端横向滚动
- [x] 第一列固定
- [x] 代码块样式
- [x] 链接样式
- [x] 图片样式
### 变体样式
- [x] 紧凑型表格(.compact)
- [x] 无边框表格(.borderless)
- [x] 高亮表格(.highlight)
### 主题一致性
- [x] 与blockquote风格一致
- [x] 与.main容器风格一致
- [x] 使用主题CSS变量
- [x] 深色模式配色协调
---
## 🎉 总结
### 设计优势
1. **完全符合主题** - 与blockquote、.main容器风格统一
2. **视觉舒适** - 毛玻璃、柔和阴影、透明边框
3. **深色模式友好** - rgba颜色、柔和过渡
4. **交互体验好** - 悬停效果、平滑动画
5. **响应式完善** - 移动端优化
6. **易于定制** - CSS变量
### 使用建议
1. **基础表格** - 使用默认样式
2. **数据密集** - 使用紧凑型(.compact)
3. **简洁风格** - 使用无边框(.borderless)
4. **重要表格** - 使用高亮(.highlight)
5. **移动端** - 自动响应式,无需特殊处理
---
**表格样式设计完成!完全符合Ying主题风格!** 🎉
**现在可以在Markdown文章中使用表格了!**
@@ -0,0 +1,200 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../../架构总览.md)。
# 📚 性能优化文档索引
**整理日期:** 2026-06-03
**项目:** Hugo博客性能优化
**优化效果:** 性能提升50%,Lighthouse 75-80分
---
## 📁 文档分类
### 🎯 一、项目总结(3份)
| 序号 | 文档名称 | 创建日期 | 说明 |
|------|---------|---------|------|
| 1 | [01-项目完成总结.md](./01-项目完成总结.md) | 2026-06-03 | 整个项目完成情况总结 |
| 2 | [02-方案1完成总结.md](./02-方案1完成总结.md) | 2026-06-03 | JS和字体优化总结 |
| 3 | [03-三步优化完整指南.md](./03-三步优化完整指南.md) | 2026-06-03 | 三步优化的完整指南 |
---
### 🔧 二、优化实施(4份)
| 序号 | 文档名称 | 创建日期 | 说明 |
|------|---------|---------|------|
| 4 | [04-JS按需加载优化.md](./04-JS按需加载优化.md) | 2026-06-03 | JS代码拆分和按需加载 |
| 5 | [05-PJAX适配说明.md](./05-PJAX适配说明.md) | 2026-06-03 | PJAX导航适配 |
| 6 | [06-PJAX修复总结.md](./06-PJAX修复总结.md) | 2026-06-03 | PJAX修复详情 |
| 7 | [07-字体子集化优化.md](./07-字体子集化优化.md) | 2026-06-03 | 字体优化方案和实施 |
---
### 🤖 三、GitHub Actions自动化(3份)
| 序号 | 文档名称 | 创建日期 | 说明 |
|------|---------|---------|------|
| 8 | [08-GitHub-Actions使用指南.md](./08-GitHub-Actions使用指南.md) | 2026-06-03 | Actions详细使用说明 |
| 9 | [09-Actions修复指南.md](./09-Actions修复指南.md) | 2026-06-03 | 常见问题和修复方法 |
| 10 | [10-提交指南.md](./10-提交指南.md) | 2026-06-03 | Git提交最佳实践 |
---
### 🧪 四、测试验证(2份)
| 序号 | 文档名称 | 创建日期 | 说明 |
|------|---------|---------|------|
| 11 | [11-JS优化测试指南.md](./11-JS优化测试指南.md) | 2026-06-03 | JS优化测试方法 |
| 12 | [12-字体优化测试指南.md](./12-字体优化测试指南.md) | 2026-06-03 | 字体优化测试方法 |
---
### 📋 五、详细方案(4份)
| 序号 | 文档名称 | 创建日期 | 说明 |
|------|---------|---------|------|
| 13 | [13-主题全面优化分析.md](./13-主题全面优化分析.md) | 2026-06-03 | Ying主题优化分析 |
| 14 | [14-JS优化最终方案.md](./14-JS优化最终方案.md) | 2026-06-03 | JS优化详细技术方案 |
| 15 | [15-字体优化手动指南.md](./15-字体优化手动指南.md) | 2026-06-03 | 字体优化手动操作 |
| 16 | [16-实施总结报告.md](./16-实施总结报告.md) | 2026-06-03 | 整体实施情况报告 |
---
## 📊 文档统计
### 按类别统计
| 类别 | 数量 | 说明 |
|------|------|------|
| 项目总结 | 3份 | 整体完成情况 |
| 优化实施 | 4份 | 具体优化方法 |
| 自动化 | 3份 | GitHub Actions |
| 测试验证 | 2份 | 测试方法和结果 |
| 详细方案 | 4份 | 技术细节 |
| **总计** | **16份** | 完整文档体系 |
---
## 🎯 快速查找指南
### 场景1:了解项目整体情况
**推荐阅读顺序:**
1. [01-项目完成总结.md](./01-项目完成总结.md) - 快速了解全貌
2. [03-三步优化完整指南.md](./03-三步优化完整指南.md) - 详细优化内容
---
### 场景2:实施JS优化
**推荐阅读顺序:**
1. [04-JS按需加载优化.md](./04-JS按需加载优化.md) - 了解优化方法
2. [05-PJAX适配说明.md](./05-PJAX适配说明.md) - 适配PJAX
3. [11-JS优化测试指南.md](./11-JS优化测试指南.md) - 测试验证
---
### 场景3:实施字体优化
**推荐阅读顺序:**
1. [07-字体子集化优化.md](./07-字体子集化优化.md) - 了解优化方法
2. [15-字体优化手动指南.md](./15-字体优化手动指南.md) - 手动操作
3. [12-字体优化测试指南.md](./12-字体优化测试指南.md) - 测试验证
---
### 场景4:配置GitHub Actions
**推荐阅读顺序:**
1. [08-GitHub-Actions使用指南.md](./08-GitHub-Actions使用指南.md) - 使用方法
2. [09-Actions修复指南.md](./09-Actions修复指南.md) - 故障排除
3. [10-提交指南.md](./10-提交指南.md) - 提交最佳实践
---
### 场景5:遇到问题需要排查
**推荐阅读顺序:**
1. [09-Actions修复指南.md](./09-Actions修复指南.md) - Actions问题
2. [06-PJAX修复总结.md](./06-PJAX修复总结.md) - PJAX问题
3. [11-JS优化测试指南.md](./11-JS优化测试指南.md) - JS问题
4. [12-字体优化测试指南.md](./12-字体优化测试指南.md) - 字体问题
---
## 📈 优化效果总览
### 性能提升数据
| 指标 | 优化前 | 优化后 | 提升 |
|------|--------|--------|------|
| **JS(首页)** | 800KB | 350KB | ⚡ -56% |
| **JS(文章页)** | 800KB | 450KB | ⚡ -44% |
| **字体** | 1.2MB | 757KB | ⚡ -37% |
| **总体资源(首页)** | ~3MB | ~1.5MB | ⚡ -50% |
| **总体资源(文章页)** | ~3MB | ~1.6MB | ⚡ -47% |
| **Lighthouse** | 60 | 75-80 | ⚡ +25-33% |
---
## 🔄 文档维护
### 定期检查(每季度)
- [ ] 检查文档是否仍然准确
- [ ] 更新过时的信息
- [ ] 添加新的优化经验
- [ ] 整理重复内容
### 文档版本控制
所有文档都包含版本信息和创建日期,便于追踪变更。
---
## 💡 使用建议
### 新手入门
1. 先阅读 [01-项目完成总结.md](./01-项目完成总结.md)
2. 了解整体优化效果
3. 根据需要查阅具体文档
### 技术实施
1. 按照对应类别的文档顺序阅读
2. 先理解原理,再动手实施
3. 遇到问题查看故障排除文档
### 日常维护
1. 无需手动维护(完全自动化)
2. 定期查看GitHub Actions运行状态
3. 每月进行一次性能测试
---
## 📞 获取帮助
### 文档未覆盖的问题
1. 查看GitHub Actions日志
2. 检查Hugo官方文档
3. 搜索相关技术问题
### 需要更新文档
1. 编辑对应的Markdown文件
2. 更新版本号和日期
3. 提交到Git仓库
---
**文档整理完成:** 2026-06-03
**总文档数:** 16份
**覆盖范围:** 完整的优化和自动化体系
**维护状态:** 生产就绪
**祝你使用愉快!** 🎉
@@ -0,0 +1,291 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的工作过程与结论**,可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 深色模式表格样式修复指南
## 🔍 问题诊断
**症状:** 深色模式表格样式没有生效
**可能原因:**
1. 浏览器缓存
2. Hugo缓存
3. CSS文件未正确更新
4. 查看的位置不正确
---
## 🚀 完整修复步骤
### 步骤1:确认CSS已更新
**我已经确认CSS文件已更新:**
```
文件:themes/Ying/assets/css/main.css
位置:第723-726行
内容:
[data-theme="dark"] .post-content th {
color: rgba(203, 213, 225, 0.85);
border-bottom-color: rgba(148, 163, 184, 0.15);
}
```
✅ **代码已正确修改**
---
### 步骤2:清理所有缓存
**执行以下命令:**
```bash
cd E:\GitHub\blog
# 1. 清理Hugo构建缓存
hugo --cleanDestinationDir
# 2. 删除public目录(如果存在)
rd /s /q public 2>nul
# 3. 删除Hugo缓存
rd /s /q resources 2>nul
# 4. 重新构建
hugo server -D --noHTTPCache
```
---
### 步骤3:浏览器强制刷新
**Chrome/Edge:**
1. 按 `F12` 打开开发者工具
2. 右键点击刷新按钮
3. 选择 **"清空缓存并硬性重新加载"**
**或者:**
- 按 `Ctrl + Shift + R`
- 或 `Ctrl + F5`
**Firefox:**
- 按 `Ctrl + Shift + R`
---
### 步骤4:禁用浏览器缓存(开发时)
**Chrome DevTools设置:**
1. 按 `F12` 打开开发者工具
2. 切换到 **Network** 面板
3. 勾选 **Disable cache**
4. 保持DevTools打开
5. 刷新页面
---
## 🧪 验证方法
### 方法1:检查CSS加载
**Chrome DevTools:**
1. 按 `F12`
2. 切换到 **Elements** 面板
3. 找到 `<link>` 标签加载CSS
4. 点击CSS链接查看内容
5. 搜索 `post-content th`
6. 确认颜色值是 `rgba(203, 213, 225, 0.85)`
### 方法2:检查元素样式
**Chrome DevTools:**
1. 按 `F12`
2. 点击 **选择元素** 工具(左上角箭头)
3. 点击表格的表头
4. 查看 **Styles** 面板
5. 找到 `color` 属性
6. 确认值
### 方法3:查看源代码
**查看CSS文件:**
```bash
# 在命令行查看
type themes\Ying\assets\css\main.css | findstr "post-content th"
```
**应该看到:**
```css
[data-theme="dark"] .post-content th {
color: rgba(203, 213, 225, 0.85);
border-bottom-color: rgba(148, 163, 184, 0.15);
}
```
---
## 🎨 当前的颜色值
### 深色模式表格配色
| 元素 | 颜色 | 说明 |
|------|------|------|
| **表头文字** | `rgba(203, 213, 225, 0.85)` | 柔和灰白 |
| **单元格文字** | `rgba(203, 213, 225, 0.8)` | 稍暗一点 |
| **表头边框** | `rgba(148, 163, 184, 0.15)` | 透明灰 |
| **单元格边框** | `rgba(148, 163, 184, 0.08)` | 更透明 |
| **表头背景** | 渐变深蓝 | `rgba(30,41,59,0.92)` → `rgba(15,23,42,0.78)` |
| **表格背景** | 深蓝毛玻璃 | `rgba(15, 23, 42, 0.95)` |
---
## 🔧 备用方案
### 如果还是没有变化
**方案A:添加更高优先级的样式**
在CSS文件末尾添加:
```css
/* 强制覆盖深色模式表格样式 */
[data-theme="dark"] .post-content table th {
color: rgba(203, 213, 225, 0.85) !important;
border-bottom-color: rgba(148, 163, 184, 0.15) !important;
}
```
**方案B:使用内联样式测试**
在Markdown文章中使用HTML:
```html
<div data-theme="dark">
<table>
<thead>
<tr>
<th style="color: rgba(203, 213, 225, 0.85);">表头</th>
</tr>
</thead>
</table>
</div>
```
---
## 📋 测试表格
### 创建测试文章
**新建文件:** `content/posts/test-table.md`
```markdown
---
title: "表格样式测试"
date: 2026-06-03
---
## 测试表格
| 项目 | 浅色模式 | 深色模式 | 状态 |
|------|---------|---------|------|
| **表头文字** | 深灰 | 柔和灰白 | 测试中 |
| **单元格文字** | 中灰 | 柔和灰白 | 测试中 |
| **边框** | 浅灰 | 透明灰 | 测试中 |
| **背景** | 白色 | 深蓝毛玻璃 | 测试中 |
### 步骤
1. 访问此页面
2. 点击头像切换深色模式
3. 检查表头文字颜色
```
---
## 🎯 检查清单
### 缓存清理
- [ ] Hugo缓存已清理
- [ ] public目录已删除
- [ ] 浏览器缓存已清理
- [ ] 强制刷新已执行
### CSS验证
- [ ] CSS文件已更新(第723-726行)
- [ ] 颜色值正确:`rgba(203, 213, 225, 0.85)`
- [ ] CSS已正确加载到浏览器
### 功能测试
- [ ] 创建了测试表格
- [ ] 切换到深色模式
- [ ] 表头文字不刺眼
- [ ] 整体视觉舒适
---
## 🔄 进一步调整
### 如果颜色还是太亮
**调整选项1(更柔和):**
```css
color: rgba(180, 190, 205, 0.8)
```
**调整选项2(更透明):**
```css
color: rgba(203, 213, 225, 0.7)
```
**调整选项3(更暗):**
```css
color: rgba(160, 175, 195, 0.85)
```
---
## 💡 常见问题
### Q1:为什么浏览器缓存这么顽固?
**A1:** 现代浏览器会积极缓存CSS/JS文件。开发时建议:
- 始终打开DevTools
- 勾选Disable cache
- 使用强制刷新
### Q2:Hugo缓存在哪里?
**A2:** Hugo缓存位置:
- Windows: `%TEMP%\hugo_cache`
- 或项目目录的 `resources` 文件夹
### Q3:如何确认CSS已加载?
**A3:** 在Chrome DevTools中:
- Elements面板 → 搜索CSS选择器
- 或Network面板 → 查看CSS请求
---
## 🎉 完成确认
### 成功标志
- ✅ 浏览器强制刷新后生效
- ✅ 表头文字为柔和灰白色
- ✅ 不刺眼,视觉舒适
- ✅ 深色模式整体协调
### 如果还是不行
**联系我,我会:**
1. 检查CSS文件完整性
2. 提供内联样式方案
3. 或创建独立的测试文件
---
**文档版本:** v1.0
**创建日期:** 2026-06-03
**适用问题:** 深色模式表格样式未生效
@@ -0,0 +1,179 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 在线编辑文章 · 集成到 api.200181.xyz/admin
> 日期:2026-10-04(定稿)
> 结论:**已实现并本地全链路验证通过。**
> 形态:前端长在现有 `api.200181.xyz/admin` 面板里(新增「文章编辑」tab),
> 后端是一个**零 npm 依赖的轻量 Docker 容器**(`editor-api/`),替代臃肿的 `write-server`。
---
## 0. 一句话
把 `write-server/` 里**只有「文章在线编辑」这一件事**剥出来,做成 200 行级的轻量后端
容器;`/admin` 面板加一个 tab,Worker 只做「登录鉴权 + 注入令牌转发」。
---
## 1. 最终架构
```
浏览器
│ (只用后台已有的管理员登录态,不需要任何新凭据)
▼
Cloudflare Worker api.200181.xyz blog-admin/src/routes/editor.ts
/api/v2/editor/* 只做两件事:
│ ① isAdminRequest() 鉴权
│ X-Editor-Token 只存在这里 ② 注入 X-Editor-Token 反代
▼
nginx post.usj.cc /editor-api/ ──► 127.0.0.1:8017
editor-api 容器(零 npm 依赖)
读写 /srv/blog/content/posts/**,
图片落文章同级目录,
发布 = git add/commit/pull --rebase/push
```
三层各自的职责被切得很干净:
| 层 | 文件 | 职责 | 故意不做的 |
|---|---|---|---|
| 前端 | `blog-admin/public/admin/admin.js`(新增 ~450 行) | 列表 / 编辑 / 图片粘贴 / 保存 / 发布 | 不碰令牌、不直连后端 |
| Worker | `blog-admin/src/routes/editor.ts`(新增,150 行) | 鉴权 + 反代,10 条路由 | 不碰仓库 |
| 后端 | `editor-api/`(新增目录) | 读写 Markdown + git | 不做账号、评论、AI、部署编排 |
---
## 2. 顺带发现的安全问题(write-server 生产环境)
评估过程中实测发现 **`write-server` 线上完全没有鉴权**(只读核实,未做任何写操作):
| 现象 | 证据 |
|---|---|
| 后端公网裸奔 | `23.254.236.47:8016` 直接返回 200 |
| 文章全量泄露 | `https://post.usj.cc/api/posts` 无凭据返回全部 133 篇 |
| `X-Auth-User` 无签名 | 只读明文用户名,可任意伪造 |
| 写操作全裸 | PUT / DELETE / upload / deploy / ai 全部无鉴权、无 middleware |
| nginx 未加 auth_basic | — |
**这正是这次重写的动机**:不是把 write-server 搬个家,而是换成一个「默认安全」的小东西——
令牌只在 Worker 里、端口只绑回环、没配令牌直接拒绝启动。
---
## 3. 改动清单
**新增**
- `editor-api/server.mjs` + `editor-api/src/{frontmatter,posts,git}.mjs` —— 零依赖后端
- `editor-api/test/{frontmatter-roundtrip,save-roundtrip,api-e2e}.mjs` —— 三个测试
- `editor-api/{Dockerfile,README.md}`、`docker-compose.editor.yml`
- `blog-admin/src/routes/editor.ts` —— Worker 反代
- `blog-admin/.dev.vars.example` 的编辑器两项
**修改**
- `blog-admin/src/index.ts` —— 注册 10 条 `/editor/*` 路由
- `blog-admin/src/types.ts` —— Env 加 `EDITOR_API_BASE` / `EDITOR_TOKEN`
- `blog-admin/wrangler.toml` —— `[vars]` 加 `EDITOR_API_BASE`
- `blog-admin/public/admin/admin.js` —— 新增「文章编辑」tab(+约 450 行)
- `blog-admin/public/admin/admin.css` —— 编辑器样式(+约 80 行)
- `.gitignore` —— 加 `.editor-tmp/` / `.editor-trash/`
---
## 4. 测试结论(全部通过)
| 测试 | 结果 | 说明 |
|---|---|---|
| `frontmatter-roundtrip.mjs` | **130/130** | split/join 逐字节还原 + parse/stringify 深相等 |
| `save-roundtrip.mjs` | **130/130** | 每篇「读出来原样存回去」sha256 不变,测完全部还原 |
| `api-e2e.mjs` | **44/44** | 鉴权/读取/回存/只改正文/改字段/新建删除/上传/git/撞名/无空行 |
| Worker 反代链路(`.editor-tmp/verify-relay.mjs`) | **26/26** | 真实走 wrangler dev,含鉴权 403 / 404 / 409 |
| 后台 UI(无头 Chrome 真点) | **全通过** | 登录→列表→打开→改→保存→发布弹层,4 张截图 |
**过程中抓到并修掉的真 bug:**
1. **front matter 后的空行**:语料里 111 篇有空行、19 篇没有;`getPost` 把前导空行剥掉后
信息丢了,`savePost` 又无条件补一个 → 那 19 篇一保存就被平白多插一个空行。
修法:`readIndex` 记 `bodyLead`,回写时按原文件风格还原。
2. **slug 撞名静默改错文件**(详见第 6 节):定位键从 slug 换成目录名。
3. CRLF/LF、块标量 chomping(`|-` / `|` / `|+`)、`getPost` 漏 `filePath`/`eol`、
重复 slug 返回 500 —— 都是早期测试抓到的。
---
## 5. 上线步骤
### ① 服务器侧(23.254.236.47)
```bash
# 1. 博客仓库 checkout 到 /srv/blog(main 分支,git 工作区)
# (原 write-server 用的那份仓库可以直接沿用,确认 remote 是 CNB + GitHub 两段)
# 2. 放 compose 与令牌
cd /srv/blog
mkdir -p /srv/editor-trash
cat > .env <<'EOF'
EDITOR_TOKEN=<openssl rand -hex 32 生成的值>
EOF
docker compose -f docker-compose.editor.yml up -d --build
curl -s http://127.0.0.1:8017/health # 应返回 {"ok":true,...}
```
### ② nginx(post.usj.cc 的 server 块里加一段)
```nginx
# —— 文章编辑后端:只给 Cloudflare Worker 反代用 ——
# 令牌本身就是鉴权(X-Editor-Token),所以这里不再叠 auth_basic。
location /editor-api/ {
proxy_pass http://127.0.0.1:8017/; # 末尾的 / 会剥掉 /editor-api 前缀
proxy_http_version 1.1;
proxy_set_header Host $host;
client_max_body_size 25m; # 图片直传
proxy_read_timeout 120s; # git push 可能慢
}
```
> 可选加固:`location` 里再叠 `allow <Cloudflare IP 段>; deny all;`,
> 让这条路径只有 CF 能碰到。令牌泄露才是真风险,这层属于纵深防御。
### ③ Cloudflare 侧
```bash
cd blog-admin
npx wrangler secret put EDITOR_TOKEN # 粘贴与服务器 .env 相同的值
npx wrangler deploy
```
`EDITOR_API_BASE` 已写在 `wrangler.toml` 的 `[vars]`(`https://post.usj.cc/editor-api`)。
### ④ 验证
打开 `https://api.200181.xyz/admin` → 「内容管理 / 文章编辑」→ 随便开一篇 → 改一个字 →
保存 → 「发布 / 同步」里确认文件出现在待发布列表。
### ⑤ 下线 write-server(**确认新编辑器好用之后再做**)
1. nginx 里摘掉 write-server 的 `location /`(先只留 `location /editor-api/`)
2. `docker stop write-server && docker update --restart=no write-server`
3. 关掉 `8016` 的公网映射(compose 里删 ports 或改绑 127.0.0.1)
4. 观察几天没问题,再删镜像与数据卷(**删前先备份**)
---
## 6. 遗留问题:slug 撞名(需要你决定)
仓库里有 **5 组**文章共用同一个 slug,Hugo 的 permalink 是 `/:slug`,所以每组里
**有一篇在线上是被另一篇覆盖掉的(打不开)**:
| slug | 两篇 |
|---|---|
| `20210901` | Twitter主题加入加载耗时… / 无悔 |
| `20211122` | 情侣恋爱倒计时小工具… / 这组照片的主题,咱就叫它光吧 |
| `20211128` | 大学生体测… / 可惜不能一直做小孩子… |
| `20211223` | 更换掉jsdelivr… / 放假之前最后一次的照片合集… |
| `20240602` | parsec远程软件报6023错误 / idea关闭ai自动补全 |
编辑器已经把这件事**标出来了**(列表里黄色「URL 冲突」标签;拿撞名 slug 去查会返回 409
并列出候选篇目,不会猜)。但**改哪一篇的 slug、还是让后写的那篇换个 slug**,需要你定。
> 顺带:改 slug = 改网址,旧链接会 404。如果在意 SEO,得配套做重定向。
@@ -0,0 +1,284 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 评论加载优化方案(Cloudflare 免费版)
> 起因:读者侧「评论加载太慢」。
> 约束:**Cloudflare 免费版**,无中国节点;全程不引回自维护机器(除非你主动选第三档)。
> 状态:**第一档 + 2-A 已实施**(本地,待预览确认);2-B 待做,2-C 已由实测数据关闭。
---
## 0. 先厘清「慢」到底慢在哪一层
这一点决定每一项优化的真实价值,否则容易做一堆「看起来很快」的改动却没有体感。
| 层 | 链路 | 现状 | 谁能修 |
|---|---|---|---|
| **L1 跨境** | 读者浏览器 → CF 边缘机房 | CF 免费版无中国节点,每次请求跨境 ≈ 250–400ms | 只能靠**少发请求**(前端),或第三档反代 |
| **L2 回源** | CF 边缘机房 → D1 主库 | D1 主库在 WNAM(美国西),代码注释:「每次读都要跨太平洋」 | **边缘缓存**(省掉这一跳) |
| **L3 结构** | CF 在中国大陆无节点 | 结构性,无解 | 只有第三档(境内反代) |
**关键结论:边缘缓存(第二档)治的是 L2 和额度,治不了 L1。** 读者感受到的「慢」主要在 L1,所以**降请求数比加缓存更直接**。
---
## 1. 当前一次评论首屏的请求链(来自代码,非推测)
Artalk 客户端 `libs/Artalk.js` 的初始化是**串行**的——已从压缩源码确认:
```js
// conf 请求(await)→ mounted → 之后才发评论列表
...getApi().conf.conf().catch(...)
... e.trigger("mounted"), e.conf.remoteConfModifier || e.fetch({offset:0})
```
即:**`conf` 不回来,评论列表请求根本不会发出**。所以首屏是严格串行的两跳:
```
浏览器 ──① GET /api/v2/conf ──────► CF 边缘 ──► D1 ≈ 1×L1 + 1×L2
──② GET /api/v2/comments ─► CF 边缘 ──► D1 ≈ 1×L1 + 1×L2(含多条查询)
──③ POST /api/v2/pages/pv ─► CF 边缘 ──► D1 (异步,不阻塞渲染)
```
页面加载时还会打 `/captcha/status` 或 `/human/*`(仅在需要时)。
**L1 一次 ≈ 250–400ms(国内实测 CF 0.75s vs EdgeOne 0.25s)**,所以 ①→② 两跳 ≈ 0.5–0.8s 纯网络。把 ① 干掉,首屏网络时间**直接减半**。
> 已有的优化不动:`window.friendLinks/friendFeeds` 是 Hugo **构建期内联**的(零运行时请求);Artalk 的 JS/CSS 走又拍云本地资源;`conf` 已有 localStorage 缓存(**但只在 PJAX 第二次以上生效,首屏吃不到**);`/api/favicon` 已有 KV 缓存 + `max-age=86400`;计数查询已有 KV 短缓存(10 分钟 + 版本号失效)。
---
## 2. ✅ 第一档(已实施):修 `preconnect`
**问题**:`themes/Ying/layouts/partials/head.html` 里给 API 域名预热了连接,但**没带 `crossorigin`**。
Artalk 发的是 CORS 跨域请求。浏览器把「带凭据的连接」和「匿名连接」分池缓存:不带 `crossorigin` 预热出来的是普通连接,**CORS 请求无法复用它**——等于白预热,还是要重付一次 DNS + TCP + TLS(约 2–3 个 RTT)。
**改法**:`preconnect ... crossorigin` + `dns-prefetch` 兜底(老浏览器不支持 preconnect);host 取 `artalk.server` 与 `rssapi.base` 去重后渲染,以后两处配置分家也不会漏。
**验证**:本地 `hugo` 构建产物确认(1270 个页面全部生效,两个 host 正确去重):
```html
<link rel="preconnect" href="https://cravatar.cn">
<link rel="preconnect" href="https://api.200181.xyz" crossorigin>
<link rel="dns-prefetch" href="https://api.200181.xyz">
```
**估算收益**:首屏第一跳省下 ~2–3 个 RTT 的握手(≈200–400ms),且**后续 ②③ 请求复用同一条连接**,各自只花 1 个 RTT。
---
## 3. 📋 第二档评估
### 2-A. ✅ 首屏内联 `conf`(已实施)
> ⚠️ **先记一次认知纠正:本节原评估的方向是错的。**
>
> 原方案写的是「前端首屏直接 `useBackendConf: false` 用内联配置起步,跳过 ①」。
> 读 `libs/Artalk.js` 压缩源码后确认**做不到**:
>
> ```js
> const { data: i } = await e.getApi().conf.conf() // ← 无条件发请求
> if (e.conf.useBackendConf) { ...merge frontend_conf... } // ← 只管要不要「用后端覆盖前端」
> ```
>
> `useBackendConf` 决定的是**要不要采用后端的配置**,与**发不发这次请求**无关。
> 所以内联配置在 Artalk 内部绕不过请求 ① —— **必须拦在 fetch 层**。
> (PJAX 缓存那条路径也没省掉请求,它省的是「重新渲染」,不是「重新请求」。)
**做法**(三段,缺一不可):
1. **构建期抓快照** —— `.cnb.yml` 的「Hugo 构建」stage 里 `curl` 一次 `/api/v2/conf`,
落到 `data/artalk_conf.json`(Hugo 读成 `site.Data.artalk_conf`)。
拉取失败就删文件降级(失败发生在 `if` 条件里,不触发 `set -e`,**绝不让它拖垮构建**)。
该文件已 gitignore:每次构建现拉,避免陈旧配置被 git 固化。
2. **模板内联** —— `partials/artalk.html` 输出 `window.__artalkConfSnapshot = {…}`,
放在 `window.artalkConfig` 之前。`</` 会被替换成 `<\/` 防 `</script>` 逃逸。
3. **fetch 拦截** —— `modules/artalk.js` 的包装层里命中 `/api/v2/conf` 时,
直接 `new Response(JSON.stringify(快照))` 返回,**完全不发网络**。
(Artalk 的通用请求函数 `w()` 只检查 `r.ok` 就把 Response 原样返回,所以合成响应可无缝接管。)
**为什么可以安全复用「一份固定响应」**:服务端 `getConf` 是
`{...DEFAULT_FRONTEND_CONF, ...settings.frontend_conf, imgUpload: imgUpload || admin}` ——
**除 `imgUpload` 外全部来自 settings 表,与访客无关**。于是:
- **匿名访客** → conf 人人相同 → 用快照 ✅
- **已登录用户** → 管理员会拿到 `imgUpload: true`(个性化)→ **放行真实请求**
判断方式:`w()` 在无 token 时会把 `Authorization` 头删掉,所以「该头存在」即「有凭据」;
判不准时保守放行(宁可多一跳,也不错配)。
**实测验证**(Chrome CDP 抓真实页面网络):
| | `/api/v2/conf` 请求数 |
|---|---|
| 改动前(对照:CDP 注入脚本吞掉快照变量的赋值) | **2 次** |
| **改动后** | **0 次** ✅ |
页面探针同时确认 Artalk 完全正常:`artalkInited: true`、`editorPresent: true`、
`listPresent: true`、`errText: ""`;且 conf 内容确实来自快照
(`sendBtn:"评论一下"` / `nestMax:2` / `locale:"zh-CN"` / `emoticons` 全对);
conf 之后的评论列表请求照常发出 —— **串行链没断**。
**成本**:conf 响应 771B,内联进 123 个带评论区的页面(占页面体积 **1.6%**),全站内容一致。
| | 结果 |
|---|---|
| 收益 | 首屏少一整跳跨境(≈250–400ms);**实际比预期多省一倍**,见下方「顺带发现」 |
| 风险 | 低。构建期失败自动降级;带凭据请求自动放行 |
| 代价 | 后台改评论配置后要等下次构建才生效(已有每日 9:00 定时构建兜底) |
**怎么回退**:删掉 `data/artalk_conf.json` 再构建即可 —— 模板不输出快照,前端自动走原生远程请求,行为等同今天(无需改代码)。
### 2-B. 给读接口加边缘缓存(Cache API)
**注意**:Worker 的响应默认**不进** Cloudflare 缓存。光加 `Cache-Control` 头是**没有效果的**,必须显式用 `caches.default.put()/match()`。(这也是为什么这项不能"顺手加个头"就完事。)
必须做对的四件事:
1. **只缓存匿名公开视图**。`listComments` 的结果依赖是否管理员(`is_pending` 过滤、`includeMarked`)、`name`/`email`、`scope=user`、`type=mine|pending|mentions`、`view_only_admin`——这些**一律不缓存**,只缓存 `scope=page` 的默认视图。`/conf` 同理(含 `imgUpload || admin`)。
2. **缓存 key 要带 origin**。`corsHeaders()` 已经把 `Access-Control-Allow-Origin` 回显成请求方的 Origin(并设了 `Vary: Origin`),缓存 key 里必须把 origin 编进去,否则 A 域名的响应会被回给 B 域名。
3. **失效策略复用现有机制**。项目里已有 `cml:ver:<site>:<page>` 这个 KV 版本号,评论新增/删除/审核会 `bumpCountVersion()`。**直接把它编进缓存 key** → 有评论变化时 key 自然变化,旧条目靠 TTL 自然过期,不用做 purge。
4. **TTL 取短**:30–60s。评论不是毫秒级强一致场景。
| | 评估 |
|---|---|
| 收益 | **D1 额度**:命中时省掉「计数 + 根列表 + 子评论 + page + site」多条查询(单页几十~几百 rows_read)。这个是真金白银——代码注释里记着当年额度被打爆过 |
| 收益 | **延迟**:命中时省掉 L2 那一跳(≈100–200ms)。但**L1 那一跳仍然要付**,所以体感提升有限 |
| 风险 | 低–中。要点全在「不可缓存哪些请求」上,漏一个就是串号/看到别人的数据 |
| 代价 | 评论提交后,**同一机房的其他读者最多看到 TTL 秒的旧列表**(可接受) |
### 2-C. Smart Placement(`[placement] mode = "smart"`)
让 Worker 跑在**离 D1 更近**的机房,代价是可能离**读者**更远。
**要不要开不用猜**:你的 `/api/v2/healthz` 已经专门返回这两个字段了(当初就是为这个判断留的):
```
colo —— Worker 实际执行机房
region —— D1 实际服务区域
```
- **同区域** → 开了没收益,还可能变慢,**别开**
- **不同区域** → 有意义,可以开
**实测结果(2026-10-04,用户提供)**:
```json
{"region":"WNAM","colo":"LAX"}
```
两者同为美西 → **结论:不开**。Worker 已经在 D1 同区域执行,Smart Placement 带不来收益,
还可能把请求推到离读者更远的机房。**本项关闭,不用再做。**
### 2-D. 「加 HTTP 缓存头」——不是独立项
它只是 2-B 的实现细节(`caches.default.put()` 要求响应显式可缓存)。**单独加头没有任何效果**,不要当成一项来做。
### 顺带发现(本轮实测捡到的既有问题,与 2-A 独立)
**① `initArtalk()` 被调用两次 → conf / comments / pv 全部发两遍。**
根因是既有代码的重复触发:
```
partials/artalk.html 内联脚本 → initArtalk 尚未定义 → 注册 DOMContentLoaded 回调(兜底)
assets/js/main.js:70 → 也在 DOMContentLoaded 里调 window.initArtalk()
↓
两个监听都会触发 → initArtalk 跑 2 次
```
CDP 实测(改动前):`GET /api/v2/conf` ×2、`GET /api/v2/comments` ×2、`POST /api/v2/pages/pv` ×2。
2-A 把 conf 那两次消掉了,但 **comments 仍多付一次跨境,`pv` 仍是双倍计数**
(**浏览量统计偏高 100%**,这是数据准确性问题,不只是性能)。
修法很轻:在 `initArtalk` 入口加幂等守卫(同一 `pageKey` 已初始化过就 `return`),
PJAX 切页时 `pageKey` 变化自然放行。**但改动落在 PJAX 路径上,建议单独一轮做 + 单独验证。**
**② `head.html:59` 有一个第三方统计脚本。**
```html
<script async src="https://019e3dec-3312-7873-862a-3f56ac99ea83.spst2.com/ustat.js"></script>
```
它不在主题自身模块里,是页面**唯一的外部脚本**,会把访问数据上报给第三方。
**确认是不是你自己加的**;若不是,建议连同这一行一起删掉。
**③ ✅ 已修:非评论类错误层会让整个评论区「假故障」一次(顿一下 + 弹「已恢复」toast)。**
**现象**:进文章页时评论区整体置灰、插「评论服务暂时不可用」横幅,约 1 秒后自己恢复并弹
`评论服务已恢复,可以继续评论了 ✓` —— 读者看到的就是「顿一下 + 一条莫名其妙的提示」。
**根因**(CDP 时间线实测,非推测):
```
表情包 /emotion/OwO.json 加载失败(本地预览跨域;线上则可能是 CDN 抖动)
→ Artalk 在「编辑器插件面板」内渲染 .atk-error-layer,文本:
Artalk Error / [表情] 加载失败: TypeError: Failed to fetch
→ rewrite() 只用 layer.querySelector('.error-message') 取文本 —— 而插件面板的错误层
没有这个 class,取到空串
→ 空串不匹配任何特征词 → 兜底 code = 'internal'
→ if (code !== 'network') setSvcDown(code) ← 只有 network 被豁免,internal 不豁免
→ 整个评论区置灰 + 插横幅(此时评论数据其实还没回来,也没失败)
→ 约 1s 后 Artalk 收掉错误层 / 评论数据正常返回 → setSvcUp() → 弹 toast
```
CDP 抓到的判定证据:错误卡片上的 `data-raw` = `"|internal"`(竖线左边就是空串 `raw`),
而错误层的真实 DOM 位置是 `atk-editor-plug-emoticons < atk-plug-panel-wrap < atk-main-editor`。
**修复**(`themes/Ying/assets/js/modules/artalk.js`,三处):
1. `rewrite()` 开头直接跳过 `.atk-plug-panel-wrap / .atk-editor-plug-emoticons` 内的错误层 ——
既不改写文案,也不参与故障判定(这类层是资源加载失败,不是评论服务故障)。
2. 故障判定加证据要求:`if (code !== 'network' && (raw || (fresh && fresh.code)))` ——
**读不到可归因证据时不下结论**,宁可只渲染卡片也不误置灰。
3. `setSvcUp()` 加最短门槛:禁用态不足 3 秒不弹 toast(真实故障不会两三秒就恢复)。
**验证**:CDP 复跑,`svc-down` / 横幅 / toast 三项全部 0 次,评论数据仍 200;
另做三组回归:真实故障层仍置灰 ✅、无文本层不置灰 ✅、插件面板层完全放行 ✅。
> ⚠️ **线上本来不出现这个现象**:线上页面在 `usj.cc`、表情包也在 `usj.cc`,同源不触发 CORS。
> 它只在本地预览(跨域)必现,线上要等 `OwO.json` 真出问题(CDN 抖动 / 404)才会踩到 ——
> 所以这算是一次「本地预览帮忙提前暴露了线上潜在缺陷」。
### 不建议做的
- **改 Artalk 客户端把 ① ② 并行**:要 fork 官方库,收益和 2-A 重叠,维护成本高。
- **给 `/pages/pv` 加速**:它已经是异步的,不阻塞渲染。
---
## 4. 建议的执行顺序
| 顺序 | 项目 | 收益 | 风险 | 状态 |
|---|---|---|---|---|
| ✅ 1 | 第一档 preconnect | 首屏 −200~400ms | 极低 | **已完成** |
| ✅ 2 | 2-A 首屏内联 conf | conf 请求 **2 次 → 0 次** | 低 | **已完成** |
| ✅ 3 | 2-C Smart Placement | — | 零 | **实测同区域 → 关闭** |
| 4 | 修「`initArtalk` 跑两次」 | 再省 1 跳,且**修正 PV 双倍计数** | 中(涉及 PJAX) | **建议做,单独一轮** |
| 5 | 2-B 读接口边缘缓存 | 保 D1 额度;命中 −100~200ms | 中 | 推荐 |
做完 2 + 4 + 5,首屏从「2 跳跨境」变成「1 跳 + 命中即回」,D1 读量大幅下降,PV 计数恢复正确。
**预期天花板**:即使全部做完,L1(跨境那一跳)依然存在——免费版 CF 在国内就是没有节点。
---
## 5. 第三档(真正的解法,需要你权衡)
`usj.cc` 的 NS 在 DNSPod,可以对 `api.usj.cc` 做**分线路解析**:境内 → 境内机器反代 → CF;境外 → 直连 CF。这才治本(L1 也消失)。
两条硬约束:
1. **`api.200181.xyz` 本身做不了**——200181.xyz 的 NS 在 Cloudflare,免费版不支持按国家分线路解析。所以必须换成 `api.usj.cc` 这类「NS 在 DNSPod」的域名。
2. **代价是把一台自维护机器重新拉回关键路径**——与 2026-10-04 刚完成的「0 台自维护机器」方向相反。
**没有白吃的午餐**:这一档能根治,但要把刚拆掉的东西装回去。
---
## 6. 不变量(别被顺手改坏)
- `SERVER_API_VERSION` 必须与博客里打包的 Artalk 客户端版本一致(当前 2.8.7),否则前端弹版本警告。
- `listComments` 里 `countFromSql` 的「不用 JOIN users 就不 JOIN」是额度优化,别回退。
- 任何新增缓存都必须先回答:**这个响应会不会因为「谁在问」而不同?** 会 → 不能缓存,或必须把身份编进 key。
@@ -0,0 +1,124 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 评论区孤儿评论修复方案(2026-10-06)
> ## ✅ 状态:**已执行完成**(2026-10-06 12:2x)
>
> - 21 条 UPDATE 全部成功,**816 条评论已迁回正确页面**
> - 22 个目标 key 条数精确到位;残留老 key 已清空
> - 端到端验证通过:`/20230424.html`(苏州一日游)已能显示评论
> - 备份:`.editor-tmp/BACKUP_816条_20261006.txt`
> - 未迁移的 13 个 key / 377 条保持原位未动
## 一句话结论
**你的评论一条都没丢。** 线上 D1 里有 **3423 条评论**,
其中 **1193 条**因为老站 URL「日期错了一天」变成了孤儿(挂在 404 页面上),
**816 条**可以精确找回,**377 条**因现站已无对应文章而保留原样。
---
## 一、线上库真实基线
```
comments 表:3423 条(存活 3380 条)
id 范围:10393 ~ 15674
94 个 page_key:
· /comment.html 留言板 1001 条
· 65 个「老站日期型」key(/20230423.html) 1975 条
· 28 个「新站时间戳型」key(/20260708110221.html) 404 条
```
**你举例的 `/20230424.html`(苏州一日游)有 40 条评论 —— 找到了,挂在 `/20230423.html` 上。**
---
## 二、问题成因
老站迁移时,**部分文章的 URL 日期错位了一天**:
| | 错误 URL(有评论) | 正确 URL(无评论) |
|---|---|---|
| 苏州一日游 | `/20230423.html`(40 条,title 显示 "404 Page not found") | `/20230424.html`(页面正常,0 条) |
| 新的开始 | `/20230527.html`(76 条,title=404) | `/20230528.html`(页面正常,0 条) |
- 评论**绑在**老 URL 上
- 页面**只认**新 URL(老 URL 返回 302 → /404.html)
- 所以评论「有数据但显示不出来」
---
## 三、修复方案(待批准执行)
**23 个孤儿 key / 816 条** → 改挂到现站正确 slug。
`UPDATE comments SET page_key='/新key' WHERE page_key='/老key'`(**按 page_key 定位,不用 id**)
| 孤儿 key | 条数 | → 目标 | 文章标题 |
|---|---|---|---|
| `/20230527.html` | 76 | `/20230528.html` | 新的开始 |
| `/20220810.html` | 58 | `/20220811.html` | typecho实现QQ头像用户评论加密 |
| `/20220906.html` | 57 | `/20220907.html` | 主力机由荣耀20切换到iPhone13 |
| `/20221102.html` | 52 | `/20221103.html` | 毕业篇:最后几个月的大学生活 |
| `/20221203.html` | 50 | `/20221204.html` | 2022·襄阳第一场雪 |
| `/20220227.html` | 48 | `/20220228.html` | 2022,除夕过后的那些事 |
| `/20211121.html` | 46 | `/20211122.html` | 这组照片的主题,咱就叫它光吧 |
| `/20220430.html` | 40 | `/20220501.html` | iphone快捷指令发布动态说说(合并) |
| **`/20230423.html`** | **40** | **`/20230424.html`** | **苏州一日游 ★** |
| `/20220503.html` | 34 | `/20220501.html` | iphone快捷指令发布动态说说(合并) |
| `/20211222.html` | 33 | `/20211223.html` | 放假之前最后一次的照片合集 |
| `/20230803.html` | 32 | `/20230804.html` | 家乡随拍 |
| `/20230125.html` | 31 | `/20230126.html` | 感谢哥哥给的网站 |
| `/20220619.html` | 30 | `/20220620.html` | 我跳绳的那些日子 |
| `/20221016.html` | 28 | `/20221017.html` | 毕业纪念篇:图书馆 |
| `/20220104.html` | 26 | `/20220102.html` | 祝大家元旦快乐 |
| `/20211113.html` | 24 | `/20211114.html` | 双十一已经变味了 |
| `/20230409.html` | 22 | `/20230410.html` | 四月随笔 |
| `/20211210.html` | 21 | `/20211211.html` | 盘点注册的域名 |
| `/20211007.html` | 19 | `/20211008.html` | 记录人生第一次洗牙 |
| `/20210911.html` | 19 | `/20210912.html` | 别让抖音支配了你的大学生活 |
| `/20221213.html` | 16 | `/20221214.html` | Apple Watch Series7 体验 |
| `/20230716.html` | 14 | `/20230717.html` | 襄阳唐城 |
✅ **已核实:18 个目标页当前评论数全部为 0** —— 不会覆盖、不会冲突。
---
## 四、不迁移的(377 条,保留原样)
现站已无对应文章,评论**保留在原位不删**(万一以后想恢复旧文章,评论还在):
| 孤儿 key | 条数 | 内容主题 |
|---|---|---|
| `/20220115.html` | 54 | 开发 app / uniapp |
| `/20220410.html` | 51 | iPhone 更新 |
| `/20211117.html` | 47 | Twitter 主题界面 |
| `/20211111.html` | 43 | 网站头像插件 |
| `/20211203.html` | 32 | 人像摄影 |
| `/20220328.html` | 32 | 青年大学习 |
| `/20211124.html` | 30 | RSS 阅读器(蚁阅) |
| `/20260724101237.html` | 24 | 流量卡/广电(现站 slug 冲突) |
| `/20211024.html` | 20 | 医疗/手术 |
| `/20211017.html` | 17 | 弟弟听力/同济医院 |
| `/20211015.html` | 14 | 恋爱话题 |
| `/20220607.html` | 11 | 腾讯游戏/王者 |
| `/202610011226.html` | 2 | Gitea 测试文 |
---
## 五、执行方式
```bash
# 1. 备份(先把这 816 条 dump 出来)
# 2. 执行 21 条 UPDATE(按 page_key 定位)
# 3. 核对:目标 slug 评论数 = 预期值;总量仍为 3423
```
SQL 在 `.editor-tmp/FINAL_SQL.json`。
---
## 六、顺带修掉的两个坑
1. `scripts/refresh_cdn.js` —— 多吉云 `rtype=path` 目录刷新**必须带尾斜杠**(已加兜底 + 日志)
2. `blog-admin/src/routes/rss/tools.ts` —— 加 `s-maxage` / `CDN-Cache-Control`,让 CF 边缘真正缓存
@@ -0,0 +1,335 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 四问诊断报告(2026-10-06)
一次性回答四个问题:CDN 刷新、KV 配额、D1 评论对账、certimate 迁移。
---
## 一、多吉云刷新 & 已删文章还能访问
### 1.1 先更正我自己的话
我一开始说「多吉云那条已经是全量(`--all`)」——**这句话是错的**,实测证据如下:
```
cnb-secrets.yml:33 CDN_URL_LIST: "https://usj.cc"
```
`scripts/refresh_cdn.js` 只做一件确定性的事:把 `CDN_URL_LIST` 按逗号/换行拆开,
逐条调 `/cdn/refresh/add.json`(`rtype: "path"`)。**当前配置里这个变量只有首页一个 URL**。
结论:**多吉云确实不是全量刷新,只刷新了首页 `https://usj.cc`。**
### 1.2 那为什么删掉的文章还能访问?
不是多吉云的问题。实测这条 URL:
```
$ curl -sI https://usj.cc/202610021614.html
HTTP/1.1 200 OK
Server: marco/3.2 ← 又拍云特征头
X-Cache-Lookup: Hit From Upstream Cluster ← 命中缓存
Last-Modified: Sun, 04 Oct 2026 14:12:26 GMT ← 旧副本时间
Cache-Control: max-age=691200 ← 8 天 TTL
Age: 131684 ← 已缓存约 1.5 天
```
判别实验(关键):
| URL | 状态 | 说明 |
|---|---|---|
| `/202610021614.html`(已删除) | **200** | 缓存里还有旧副本 |
| `/zzz-not-exist-111.html`(从未存在) | **302 → /404.html** | 源站没这个文件 |
两者行为不同 ⇒ 说明**源站确实已经没有这个文件了**(本地 `content/` 和 `public/` 均已确认无此页),
现在返回 200 完全是**又拍云上的 8 天旧副本**。
### 1.3 根因:两条 CDN 线路都刷不到「已删除页」
`scripts/purge_list.js` 的逻辑是:遍历 **构建产物 `public/`**,把里面所有 `.html` 列出来提交刷新。
```js
// 全量时 walkHtml('public') —— 遍历构建产物
// 已删除的页面自然不在里面 → 永远不会被提交刷新
```
也就是说:**刷新清单只会包含「现在存在的页面」**。一个页面被删掉后,
它就从清单里消失了,于是 CDN 上的旧副本只能等 TTL 自然过期。
- 又拍云:HTML `max-age=691200` = **8 天**
- 多吉云:只刷首页,问题更明显
### 1.4 处置建议
| 方案 | 说明 | 成本 |
|---|---|---|
| **A. 什么都不做** | 等 8 天自然过期 | 0 |
| **B. 手动刷新一次**(推荐) | 把已知的已删 URL 提一次刷新,立刻生效 | 一次性 |
| C. 流水线加「删除清单」 | 构建时对比上一次产物,把消失的 `.html` 追加进 purge 清单 | 改脚本 |
对已删文章这种低频事件,**B 足够**。需要的话我可以立刻把已知的那几个已删 URL 提交到又拍云+多吉云。
---
## 二、KV 配额告警:需要调整吗?
### 2.1 告警内容
> 已使用 Workers KV 免费套餐每日限制的 **50%**,超限将返回 429;重置时间 2026-10-06 00:00 UTC。
> 免费额度:读 100,000/天、写 1,000/天、删 1,000/天、list 1,000/天。
### 2.2 谁在吃配额?(实测 4 类来源)
| 来源 | 代码位置 | 频率 | 影响 |
|---|---|---|---|
| **评论数缓存** | `comments.ts:180/206` | 每次评论列表请求,miss 时写(TTL 600s) | 读 + 写 |
| **评论数版本号** | `comments.ts:53/61` | 评论增删时写 | 写 |
| **friend-link favicon** | `routes/rss/tools.ts:86` | **每次访问友链/友圈页,每个友链一次** | **读(大户)** |
| 人机验证 / 通知节流 | `human.ts` / `admin-notify.ts` | 低 | 少量 |
**读数最大的就是 favicon 接口。** 友链共 54 条(可见 42 条),其中 15 条头像走 `/api/favicon`:
```js
// circles.html / linkify.js
img.src = RSS_API_BASE + '/api/favicon?url=' + encodeURIComponent(target);
```
每刷一次友圈/友链页 → 触发 N 次 `/api/favicon` → 每次至少 1 次 KV `get`。
favicon 本体有 24h 浏览器缓存,但**首访、清缓存、换设备都会重新打**。
### 2.3 建议:**暂时不用调整,但建议做一个小优化**
- **读 50% 是「接近」不是「超了」**,且每天 UTC 0 点清零。按当前站点的访问量,正常不会打穿。
- 即使打穿,后果是 `/api/favicon` 走兜底字母图、评论数退化为实时查库——**不是灾难性故障**。
- 免费版写限额 1,000/天,比读更容易被评论数缓存写穿。真正的风险点是**评论突然暴涨**或**有人刷评论**。
**可做的小优化(成本低、收益直接):**
1. favicon 接口响应已经带了 `Cache-Control: max-age=86400`,可以加一层 **Cloudflare Cache Rule**,
让 `/api/favicon*` 在边缘缓存 24h,这样 KV 读基本归零。
2. 若担心写限额,把 `cml:ver:` 的写入合并/延迟(比如 5 分钟内同页重复 bump 只写一次)。
**要不要升级到付费($5/月)?** 目前**不需要**。等到真收到「已超限」而不是「接近」的邮件再说。
---
## 三、D1 评论对账:老文章评论为什么没了
### 3.1 先说结论(这个和你想的不一样)
原库 **3979 条**评论 / 线上 **3423 条**。乍看差 556 条,但拆开看,**「缺失」的绝大部分根本不是评论**:
| 项 | 数量 |
|---|---|
| 线上缺失合计 | **711 条 / 63 页** |
| 其中 **点赞型记录**(内容含 `[LIKE]`) | **494 条(69%)** |
| → **真实评论型缺失** | **217 条** |
**关键发现:线上 `[LIKE]` 记录为 0 条。**
```
$ SELECT SUM(CASE WHEN content LIKE '%[LIKE]%' THEN 1 ELSE 0 END) FROM comments;
→ 0
```
说明当年数据导入时,**已经主动过滤掉了所有点赞型记录**——这是正确的设计(点赞不该占用评论表)。
所以拿原库和线上直接比条数,本身就会假性多出 494 条。
### 3.2 缺失的 217 条真实评论,按站点拆
| 站 | 缺失 | 其中点赞 | 真实 |
|---|---|---|---|
| 伍比贰 | 521 | 431 | **90** |
| 优世界 | 101 | 0 | **101** |
| 演示站 | 48 | 41 | 7 |
| 往后 | 41 | 22 | 19 |
**注意:「伍比贰」「演示站」「往后」是另外的站点,不属于 usj.cc。** 它们的数据本来就不该进本站。
### 3.3 「优世界」那 101 条对得上现站吗?
逐页核对,结果是 **一页都对不上**:
| page_key | 条数 | 现站情况 |
|---|---|---|
| `/yl` | 48 | 页面不存在(是 2022 年的旧友链页) |
| `/20241018-some-useful-win-tools` | 12 | 文章还在,但 slug 已改为 `20241019`,且**是草稿**(未发布) |
| `/27`、`/13`、`/m/17` 等 | 41 | 不存在(旧站的分页/短链) |
| `/20240921-clock-in` | 5 | 文章已删除 |
**根因不是「key 对不上」,而是这些文章本身已经不存在了。**
评论是挂在「已删除文章」上的,key 再怎么规范化也接不回来。
### 3.4 站上现存页面 vs 线上 —— 有没有真的错位?
**没有。** 用现站 284 个 slug 逐一匹配线上缺失的 63 个 key:
```
能对上现存页面: 1 页 / 4 条 (只有 /about.html)
对不上 : 62 页 / 707 条
```
**唯一一个疑似错位:`/about.html`(4 条)**——但深挖后发现**它也不需要修**:
```
id=14229 /about.html [伍比贰] 2026-02-07 👍 已点赞 Cool [LIKE]
id=14268 /about.html [伍比贰] 2026-02-12 👍 已点赞 Interesting [LIKE]
id=14269 /about.html [伍比贰] 2026-02-12 👍 已点赞 很棒的文章! [LIKE]
id=14276 /about.html [伍比贰] 2026-02-13 👍 已点赞 写得很好 [LIKE]
```
这 4 条全是**点赞型记录**,而且属于**「伍比贰」站**,本就不该进本站。
→ **结论:线上 D1 没有任何错位,完全不需要修复。**
### 3.5 线上「多出」的 7 页是什么?
| page_key | 条数 |
|---|---|
| `/20260630162329.html` | 25 |
| `/20260724101237.html` | 24 |
| `/20211001.html` | 14 |
| `/20261005113439.html` | 13 |
| `/20260629223659.html` | 10 |
| `/202610011226.html` | 2 |
| `/20261005213010.html` | 2 |
这些是**导出 db 之后新增的评论**(2026-06 之后),属于正常增长,不用动。
### 3.6 修复建议
**结论:线上 D1 不需要做任何修复。**
你的直觉「很多评论没跟页面 key 对应上、老文章没评论了」——
真实情况是:**那些评论对应的文章,本身已经不在了**(已删除 / 仍是草稿 / 属于别的站),
key 再怎么规范化也接不回来。线上数据是**干净的**。
**不建议做的**:
- ❌ 批量把「伍比贰/演示站/往后」的评论导进来 —— 那是别的站的数据(含大量点赞记录)
- ❌ 把 `/yl`、`/27` 等改成别的 key —— 没有正确的目标,改了反而污染
- ❌ 恢复 494 条点赞记录 —— 线上设计本就不存点赞(`[LIKE]` 记录当前为 0)
**如果你更看重「把老评论展示出来」**,唯一的正路是**重建这些页面**
(把已删文章从 git 历史恢复,或用 slug 重定向)。这个要单独评估,工作量比改 key 大得多。
**但请注意**:即使恢复了页面,能救回的也只是极小一部分——
「优世界」站真实缺失的 101 条里,48 条属于 `/yl`(2022 年旧友链页),
41 条属于 `/27` `/13` `/m/17` 等旧站分页短链,只有 12 条是正经文章评论。
### 3.7 那「评论少了」的体感从哪来?
很可能来自这两点(都正常):
1. **点赞不再算评论**:原库 494 条点赞在导入时被过滤,前端看到的评论数自然变少。
2. **老文章页面已删**:文章没了,评论区自然也没了。
如果确实想提升「评论数」,更实际的做法是**在新文章上做引导**,而不是抢救旧数据。
---
## 四、certimate 能否迁到 Cloudflare
### 4.1 你现在的架构
```
certimate(跑在某台服务器上)
├─ 申请:litessl CA,*.usj.cc + usj.cc,DNS-01,tencentcloud-dns
├─ 部署:dogecloud-cdn(多吉云 CDN)
└─ 部署:1panel website(那台服务器上的站点)
定时:10 12 * * *(每天 UTC 12:10)
失败邮件:177018615@qq.com
```
当前线上证书实测:
```
issuer = LiteSSL RSA CA 2025(TrustAsia)
subject = CN=usj.cc
SAN = usj.cc, *.usj.cc
有效期 = 2026-09-08 → 2026-12-07
```
### 4.2 certimate 支持 Cloudflare 吗?
**支持,而且支持两种角色:**
| 角色 | 支持 | 说明 |
|---|---|---|
| **DNS provider(申请用)** | ✅ | 填 Cloudflare API Token 即可,用于 DNS-01 验证 |
| **Deploy provider(部署用)** | ✅ | 可将证书部署到 Cloudflare |
(certimate 官方:60+ DNS 托管商、120+ 部署目标,Cloudflare 在列。)
### 4.3 你的真实目标:「把服务器干掉」
需要先厘清一件事:**usj.cc 现在到底谁在提供 HTTPS?**
从实测响应头看:
```
Server: marco/3.2 ← 又拍云
Via: T.206.M, V.403-zj-fud-202, ... ← 又拍云多级节点
```
**对外服务的是又拍云 CDN**,不是那台服务器。那台服务器承担的是:
- 跑 certimate(证书申请+分发)
- 1Panel 上的 write-server / editor-api 等自建服务
所以「干掉服务器」要分清两件事:
**① 只干掉「证书维护」这件事** → **完全可以,用 Cloudflare 托管 DNS + certimate 的 CF provider。**
路径:
1. 把 `usj.cc` 的 **DNS 托管迁到 Cloudflare**(当前在腾讯云 DNSPod)
2. certimate 里把「DNS 提供商」从 `tencentcloud-dns` 换成 `cloudflare`(填 CF API Token)
3. 证书申请照旧(litessl 或换 Let's Encrypt),DNS-01 走 CF API
4. 部署目标改为「Cloudflare」(如果把站点也迁到 CF)或保留又拍云/多吉云
**② 连「站点托管」也迁到 Cloudflare** → **可行但有取舍:**
| 优点 | 缺点 |
|---|---|
| CF 免费版自带 Universal SSL(免申请免续期) | 国内访问速度**不如又拍云/多吉云**(CF 免费版节点在境外) |
| 不用再管 90 天续期 | 免费版 Universal SSL 只覆盖 `example.com` + `*.example.com` |
| 有 CF Origin CA(15 年有效期)给回源用 | 想用 CF 自签源站证书需另配 |
### 4.4 ⚠️ 关键提醒:国内访问速度
你的读者主要在国内。**又拍云/多吉云是国内 CDN,Cloudflare 免费版是境外节点**——
如果为了省一台服务器而把整个站点切到 CF,**国内访问大概率变慢**(首包延迟从几十 ms 变成几百 ms)。
**所以我建议:**
- ✅ **可以做的**:把 `usj.cc` 的 DNS 托管迁到 Cloudflare(CF DNS 免费、管理方便、API 完善),
certimate 用它做 DNS-01,证书照旧签发后部署到**又拍云 + 多吉云**。这样证书维护不再依赖服务器上的 1Panel 部署环节。
- ⚠️ **建议保留又拍云/多吉云做 CDN**,不要为了「干掉服务器」把站点也搬到 CF。
- ❌ 如果那台服务器还跑着 write-server / editor-api,**那就不能关**——关之前先确认没有别的服务在跑。
### 4.5 你现在到底能不能关服务器?
需要先回答这个问题:**那台服务器上除了 certimate,还跑着什么?**
- 如果只有 certimate → 迁完就能关 ✅
- 如果还有 write-server / editor-api(按记忆应该有)→ **关不掉** ⚠️
建议你在关之前先列一下那台机器上的服务清单。需要的话我可以帮你连上去盘一遍。
---
## 附:本次用到的排查命令
```bash
# CDN 缓存判别
curl -sI https://usj.cc/202610021614.html | grep -iE "server|age|last-modified|x-cache"
# D1 查询(curl 走 IPv4,Node fetch 会 ENETUNREACH)
curl -s -X POST \
"https://api.cloudflare.com/client/v4/accounts/$ACC/d1/database/$DB/query" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"sql":"SELECT page_key, COUNT(*) FROM comments GROUP BY page_key"}'
# 线上是否有点赞记录
# → SELECT SUM(CASE WHEN content LIKE '%[LIKE]%' THEN 1 ELSE 0 END) FROM comments; → 0
```
@@ -0,0 +1,237 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# EdgeOne 双区域方案评估
> 2026-10-04 · 针对「境外用 EdgeOne 国际站、境内改用 EdgeOne 国内站」的构想
> 以及「write-server 拆成后端服务 + 前端与 blog-admin/Worker 合并」的可行性
## 一、结论先说
**方案成立,而且比 Cloudflare 方案好。** 三个理由:
1. **不违反任何服务条款** —— CF 要加速国内只能走「优选 IP」那条明文违规的路,EdgeOne 是正规产品能力。
2. **国内速度是量级差异** —— 备案域名的 EdgeOne 大陆节点实测 **50–90ms**;你现在 CF 后端实测 **750ms**(差 8–10 倍)。
3. **你已经在用了** —— `deploy-edgeone` job 现在就部署在这上面(`edgeone pages deploy --area overseas`),迁移成本接近零。
**而且我发现了你可能没注意到的一件事**:EdgeOne Pages 有个 `--area` 参数,你现在用的是 `--area overseas`(**主动排除了中国大陆**)。这可能意味着你**只要改一个参数**就能达到目的,而不必新建一个项目。详见第三章。
---
## 二、你的备案状态(关键前提,已核实)
- 站点页脚:**鄂ICP备2022015735号-1**
- 官方文档明确:*当项目的加速区域为「中国大陆可用区」或「全球可用区(含中国大陆)」时,添加的域名必须完成 ICP 备案*
- **你的域名已备案 → 具备启用大陆节点的资格** ✅
这一条是整个方案成立的地基。反过来说,如果没备案,这条路直接堵死(只能用海外节点,国内 670–1400ms)。
---
## 三、★ 关键发现:`--area` 参数
EdgeOne Pages 的 CLI 部署命令带一个区域参数:
| 参数 | 含义 |
|---|---|
| `--area overseas` | 只覆盖境外,**不含中国大陆** ← **你现在用的就是这个** |
| `-a global` | 覆盖全球 **+ 中国大陆**(需备案域名绑自定义域名) |
也就是说,你现在这个 `hugo-blog` 项目**是刻意把大陆排除在外的**。于是有两条路:
### 路 1(最省):单项目改参数
把 CI 里那一行改成 `-a global`,一个项目、一套配置、一个 token,同时覆盖境内外。
- 好处:**零新增组件**,改动只有一个参数
- 风险:**境外质量可能下降**。有多位站长实测反馈,global 模式下「海外就差点意思,只有一个节点,走 Anycast 网络」——这可能正是你当初选 `overseas` 的原因
- **必须实测**:改完之后用境外节点测一次,别只看国内变快了
### 路 2(你提的):双项目
- **国际站项目**(`console.intl.cloud.tencent.com`,免备案)→ `--area overseas`,保境外质量
- **国内站项目**(`console.cloud.tencent.com`,需实名+备案)→ `-a global`,保大陆速度
- 两个控制台、两套 token、两次部署
> ⚠️ **一个待实测的疑点**:官方文档里,国际站的 Pages 与国内站的 Pages 对「大陆节点」的支持描述**互相矛盾**(国际站文档说 `-a global` 含大陆,产品介绍又说国际站不含国内节点)。**结论不明,必须自己测一次**。测法见第七章。
---
## 四、EdgeOne Pages 免费额度(官方定价页)
| 项目 | 免费额度 | 你的用量 | 够吗 |
|---|---|---|---|
| 安全加速流量 | **不限** | — | ✅ |
| 安全加速请求数 | **不限** | — | ✅ |
| 构建次数 | **500 次/月** | 约 60–90 次 | ✅ |
| Edge Functions 请求 | 300 万/月 | — | ✅ |
| Cloud Functions 请求 | 100 万/月 | — | ✅ |
| KV 存储 | 1 GB | — | ✅ |
| 站点数 | 1 个主域名 + 200 子域名 | — | ✅ |
| 超出用量 | **收费版推出前不中断服务** | — | ✅ |
**唯一的硬限制(重要)**:
> **单文件、单线程限速 4 Mbps(约 500 KB/s)** —— 多位站长实测确认,官方未披露。
要正确理解它:
- 是**单文件单线程**限速,**不是整站带宽限速**。并发请求互不影响
- 你的 `zql-v2.woff2` 字体 1.2 MB → 首次加载约 **2.4 秒**(之后走缓存,不再走网络)
- 那 7 个 mp4 里最大的 12.8 MB → 首次加载约 **25 秒**(这是这个方案最实际的痛点)
- 腾讯云自家 CDN 也有这个限制,属地是「免费套餐的取舍」,不是 bug
**如果视频是核心内容**,这一条要认真权衡;如果只是少量点缀,影响可控。
---
## 五、能砍掉什么
对比**现在的架构**(EdgeOne 国际 + 又拍云 + 多吉云 + 广州中转机 + sync.sh 轮询):
| 组件 | 现架构 | EdgeOne 双区域 |
|---|---|---|
| 境外 CDN | EdgeOne 国际站 | EdgeOne 国际站(不变) |
| 境内 CDN | 又拍云 + 多吉云 | **EdgeOne 国内站** |
| 产物搬运 | 广州中转机 + `sync.sh` 每分钟轮询 | **删除** |
| 产物中转 | 腾讯云 COS | 已砍(进行中) |
| 凭据面 | 又拍云密码 + 多吉云 SK(明文写在 sync.sh) | **删除** |
| 厂商数 | EdgeOne + 又拍云 + 多吉云 + 腾讯云 COS | **腾讯云一家** |
**净收益**:少 2 家厂商、少 1 台常驻机器、少 1 个每分钟跑的轮询脚本、少 2 套硬编码凭据。
**发布链路从 7 环节降到 4 环节**(写作 → 构建 → 双区域部署 → 通知)。
---
## 六、write-server 拆分评估(你问的第二件事)
### 6.1 结论:技术可行,而且**必须拆**才能上边缘平台
这不是"能不能"的问题,而是**只能这样**。EdgeOne Pages(和 CF Workers 一样)的运行时限制是硬的:
- **无持久化文件系统** —— 无法读写 `content/` 下的 `.md`
- **不能执行 git** —— 无法 `git pull/push`
- 单次函数执行有超时(通常 30s)
而 `write-server` 的核心恰恰是这两件事:**读写本地 md 文件 + git 操作**。
### 6.2 现状的耦合点(已扫描)
```
write-server/src/
app/{edit,login,posts,images,links,feeds,comments,users,bot,wechat}/ ← 页面(可上前端)
app/api/ ← 23 个 API 路由
lib/
posts.ts ← fs 读写 md ⛔ 必须留后端
git.ts ← child_process git ⛔ 必须留后端
hugo.ts ← 调 hugo 二进制 ⛔ 必须留后端
recycle.ts ← fs ⛔ 必须留后端
auth.ts ← fs(用户表) ⛔ 必须留后端
bot/* ← 子进程轮询 ⛔ 必须留后端
ai.ts / artalk.ts / rssapi.ts / wechat.ts / fetch.ts ← 纯 HTTP(可两边)
```
**好消息**:页面与 API 已经分得比较清楚,`src/lib` 里哪些能上边缘、哪些必须留后端,界限是清晰的。这不是一个烂摊子。
### 6.3 拆分后的形态
```
前端(EdgeOne Pages / CF Worker)
React SPA(由现有页面改造)+ 调用后端 API
│
▼
后端(一台有文件系统的服务器)
Node 服务 + 复用现有 src/lib(几乎不用改)
→ 读写 md、跑 git、调 hugo、发 RSS
```
必须改的四件事:
1. **认证**:现在是同域 session/cookie,分离后要改成 token(Bearer / JWT)+ CORS
2. **数据获取**:服务端组件直读 md → 改成客户端 fetch API(多一次往返,但要加 loading 态)
3. **图片上传**:写本地磁盘 → 改成对象存储(又拍云 / R2 / EdgeOne KV)
4. **API 地址配置化**:`src/lib/config.ts` 里加后端 base URL
### 6.4 ⚠️ 但先问一句:**为什么要拆?**
如果目的是**"一个统一的管理入口"**(体验问题),那拆分会**让架构变复杂**——从 1 个应用变成 2 个(前端 + 后端),组件数不减反增。这跟你要的"简化"是反方向。
**更省的做法(零重构)**:用**一个域名 + 路径分流**把两个后台挂在一起。
```
admin.usj.cc/ → blog-admin(CF Worker,评论/RSS 管理)
admin.usj.cc/write/ → write-server(写作后台)
```
三种实现,任选:
- **OpenResty 反代**(你国内机已经有):加几行 `location /write/ { proxy_pass ...; }`,**成本最低**
- **EdgeOne 规则引擎**:用 URL 重定向 / 路径规则做分流
- **CF Worker 薄代理层**:能做,但 CF 国内慢,不适合做入口
**判断标准**:
- 只要"一个入口" → **反代,别重构**
- 真的需要"前端独立部署到边缘、后端瘦成纯 API" → 才做拆分,且要接受工作量和组件数增加
---
## 七、建议的推进顺序
### 第 0 步(今天就能做):一次实验,验证单项目可行性
这是**成本最低、信息量最大**的一步,只需改一个参数:
```bash
# 用已有的 token,把产物部署到 global 区域(覆盖大陆)测一次
npx edgeone pages deploy ./public -n hugo-blog -t "$EDGEONE_API_TOKEN" -a global
```
然后用国内机实测(可复用我之前写的 `probe_speed.py` 那套):
- 大陆解析到的 IP 是不是国内 IP
- TTFB 是否降到 100ms 级
**如果单项目 `-a global` 就达标 → 方案收敛为「改一个参数」,收工。**
如果不达标或影响境外 → 再走双项目。
### 第 1 步:确定区域策略
根据第 0 步结果,二选一:
- 单项目 `-a global`
- 双项目(国际站 + 国内站,两套 token)
### 第 2 步:接上大陆域名
国内站的 Pages 项目绑定自定义域名,走 ICP 备案校验(你已有备案号)。
### 第 3 步:砍掉旧链路
确认 EdgeOne 双区域稳定运行 **1–2 周**后,再依次停掉:
- 广州中转机的 `sync.sh` 定时任务(1Panel 里 `id=15` 那个每分钟的)
- 又拍云、多吉云的同步逻辑
- `deploy.yml` 里对应的通知与检查步骤
**不要跳步**,也不要和"write-server 重构"混在同一次变更里。
### 第 4 步(可选,独立评估):write-server
先回答 6.4 那个问题。如果要拆,它是一次独立的重构工程,单独排期。
---
## 八、待核实项(我标出来,不装作确定)
| # | 事项 | 状态 | 怎么验 |
|---|---|---|---|
| 1 | 国际站 Pages 的 `-a global` 是否真能启用大陆节点 | **文档矛盾,待实测** | 第七章第 0 步 |
| 2 | `--area overseas` 当初是否是刻意选择(境外质量考虑) | 待你确认 | 问你自己 |
| 3 | 500KB/s 单文件限速对 7 个 mp4 的实际影响 | 已知限制,待评估 | 测一个最大视频的首字节 |
| 4 | 国内站的备案接入校验是否需要变更接入商 | 待核实 | 绑定域名时会暴露 |
| 5 | EdgeOne Pages 的 Git 集成是否支持 Gitee | 有第三方提到,**未官方确认** | 若支持,可再砍掉 Gitea + act_runner |
> 第 5 条如果成立,价值很大:**Gitee 是国内平台**,用它的 Git 集成自动构建,
> 就能把自建 Gitea + act_runner + 中转机整条链一起砍掉。但需要先确认支持情况。
---
## 九、一句话总结
**你的方向是对的,而且比 CF 方案好——不违规、快 8–10 倍、你已经在用了。**
**但在动手建第二个项目之前,先花十分钟做第七章那个「改一个参数」的实验。**
它可能直接告诉你:**这件事根本不需要两个项目。**
@@ -0,0 +1,159 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# Gitee 方案评估
> 回答:**「Gitee 算不算一个替代方案?」**
>
> **结论**:算,而且它是唯一一个**官方支持作为 EdgeOne Pages 代码源**的国内平台。
> 但有一道硬门槛 —— **Gitee 免费版单仓库上限 500MB,你的 `.git` 实测 588MB,超了 17.6%**。
> 好消息是:**这道门槛跨得过去**(见第四节)。真正要权衡的是另一件事 —— **内容审查**。
---
## 一、实测数据(刚量的,不是估算)
| 指标 | 实测值 |
|---|---|
| `.git` 总体积 | **588 MB** |
| `git count-objects` size-pack | **580 MB**(对象净体积,说明已经打包过了) |
| 提交总数 | 955 |
| **HEAD 中被跟踪文件合计** | **372.9 MB / 2427 个文件** |
| HEAD 中 >1MB 的文件 | 65 个,合计 143.9 MB |
**关键推论**:`580MB(pack) − 373MB(HEAD) ≈ 207MB` 是**历史里的旧版本 / 已删除对象**。
也就是说,**光留当前状态只需要 373MB** —— 这一条决定了瘦身可行。
---
## 二、四条硬指标逐项核对
Gitee 官方配额(help.gitee.com/account/usage-quota):
| Gitee 免费版限制 | 官方值 | 你的情况 | 判定 |
|---|---|---|---|
| 单仓库容量 | **≤ 500 MB** | 588 MB | ❌ **超限 17.6%** |
| 单文件大小 | ≤ 50 MB | 最大 12.8 MB(mp4) | ✅ 安全 |
| 用户总仓库容量 | 5 GB | 588 MB(瘦身后 373 MB) | ✅ 安全 |
| 私有仓库协作人数 | 5 人 | 个人 | ✅ 无关 |
| 仓库数量 | 1000 个 | 1 个 | ✅ 无关 |
**只有一条不过:仓库体积。** 而且它刚好卡在一条"能解"的线上。
---
## 三、一个被大多数人算错的点:Gitee 方案**不消耗** Gitee Go 的额度
Gitee Go 免费额度很小(单仓库 200 分钟永久 + 每月 500 分钟)。**但这跟你的方案无关** ——
因为你走的是 **EdgeOne Pages 的 Git 集成**:代码放 Gitee,**构建跑在 EdgeOne 侧**(4 核 6GB,500 次/月)。
Gitee 在这里**只当代码网盘**,一次构建都不消耗它的 CI 分钟。
> ⚠️ 同理:**不要用 Gitee Pages**。它的构建能力和额度都远不如 EdgeOne,
> 而且 2022 年那次全站审查的触发点之一就是 Pages。
---
## 四、唯一的硬伤与解法:588MB → 500MB 以下
`.git` 588MB,其中 **207MB 是历史垃圾**(旧版本 + 已删除文件)。三种解法:
### 方案 A:只保留当前状态(最彻底,推荐)
重建一个只有 HEAD 的仓库 → `.git` 降到 **≈ 373MB**,**留 25% 余量**。
代价:**955 个提交历史会丢**。
但这在你这儿几乎无损失 —— 历史仍在自建 Gitea 那边完整保留,Gitee 只当"构建用的镜像"。
```bash
# 思路(务必先在副本上试,不要直接动主仓库)
git checkout --orphan clean-tmp
git add -A && git commit -m "squash: 当前站点状态"
git branch -D main && git branch -m main
git reflog expire --expire=now --all && git gc --prune=now
```
### 方案 B:保留历史,只清理历史中的大文件
`git filter-repo --analyze` 先定位占空间的历史大文件,再针对性剔除。
体积介于 373–580MB 之间,**需要实测才知道能不能压到 500MB 以下**,不保证成功。
### 方案 C:升级 Gitee 付费版
标准版单仓库 ≤ 1GB,1298 元/年起 —— **为了一个博客每年花 1300,不值得**。
**结论:只有方案 A 是确定可行的,而且可接受。**
---
## 五、真正要权衡的:内容审查(这条比 500MB 更重要)
Gitee 不是中立的文件存储,它有**实名认证 + 内容审查体系**。有实锤的历史:
- **2022-05-19,Gitee 把全部约 1000 万个公开仓库临时私有化,做强制审核**,合规的才恢复。
开发者必须逐个提交申请、人工复核。有用户 24 个仓库里被恢复了 22 个。
- 审查范围包括仓库名、注释、变量名 —— 有开发者抱怨"写代码时还要想这个词会不会触发敏感词列表"。
- 常见封号原因:仓库含违规内容(赌博/诈骗/侵权/涉政)、批量建仓被判滥用、开 Pages 发不合规内容。
- Gitee 数据全部境内存储,实名是硬要求(这是它拿政府/信创订单的前提)。
### 但落到你身上,风险评估是**低**的
理由三条:
1. **你的内容是技术博客** —— Android 模块化魔改、K3s、Docker、自建服务。这类内容不在敏感区间。
2. **把仓库设为私有**。审查压力的大头在**公开仓库**上(2022 那次动的就是公开仓库)。
你要用 EdgeOne 构建,私有仓库完全可以(授权访问即可)。
3. **国内平台在"政策风险"上对你反而是加分项** —— 你 GitHub 账号被标记,
正说明境外平台对你有不确定性。Gitee/CNB 的规则是**明确、可预期**的:
别传违禁内容就不会出事,而不是"不知道哪天被风控"。
---
## 六、三个国内/境外代码源横向对比
| | **Gitee** | **CNB(cnb.cool)** | GitLab.com |
|---|---|---|---|
| 仓库容量 | 500 MB ⚠️ **需瘦身** | **100 GiB** ✅ 不用动 | 2 GB ✅ |
| EdgeOne Pages 官方支持 | ✅ 明确支持 | ✅(有官方插件文档) | ✅ 明确支持 |
| 构建额度消耗 | 不消耗(构建在 EO 侧) | 不消耗 / 或用它自己的 160 核时 | 不消耗 |
| 登录 | 实名注册 | 微信扫码 | 邮箱 |
| 国内推送速度 | 快 | 快 | 跨境,588MB 首次推送慢 |
| 内容审查 | ⚠️ 有实锤事件 | ⚠️ 官方明写"平台级内容安全审查" | 无(但美国平台) |
| 政策风险 | 低(国内合规) | 低 | ⚠️ **与 GitHub 同源风险** |
| 与 EdgeOne 的距离 | 第三方 | **同为腾讯体系** | 第三方 |
### 排序建议
1. **CNB** —— 100GiB 不用瘦身,零改造,和 EdgeOne 同属腾讯、有官方集成文档
2. **Gitee** —— 官方支持、国内快,但**要先做一次历史瘦身**(一次性工作,可接受)
3. **GitLab.com** —— 容量够,但**美国平台,和 GitHub 同类风险**,且首次推送跨境 588MB 很慢
**Gitee 输给 CNB 的唯一一点就是那 500MB**,如果你不想动仓库历史,选 CNB;
如果你更信任 Gitee 这个老牌子、也愿意做一次瘦身,Gitee 完全可用。
---
## 七、Gitee 方案的完整形态
```
write-server / 本地 hugo → push Gitee(私有仓库)
→ EdgeOne Pages Git 集成自动构建(4核6G,500 次/月,不消耗 Gitee CI 额度)
→ EdgeOne 国际站(境外解析) + EdgeOne 国内站 Makers(境内解析,绑备案域名)
```
砍掉:act_runner、广州中转机、sync.sh、COS、Gitea artifact 链、8 处 Gitea 兼容分支、
又拍云、多吉云、两套冗余通知。
**3 台机器 → 0 台;7 环节 → 3 环节。**
---
## 八、前置动作(按顺序)
1. **先备份**:整仓 `cp -r` 一份(或 `git bundle create` 全量)
2. **在副本上做瘦身**,验证 `.git` 能压到 500MB 以下
3. 注册 Gitee,建**私有**仓库,首次推送验证体积
4. 在 EdgeOne Pages 里接 Gitee 仓库,跑一次构建
5. 确认无误后,才把主仓 remote 切过去
**第 1、2 步不做,其余都别动。**
---
*本文档基于 2026-10-04 实测数据与官方文档。仓库体积为 `git count-objects -vH` 实测,非估算。*
@@ -0,0 +1,202 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 代码源与构建平台选型
> 回答的问题:**「想简化是不是只能走 EdgeOne?」**
>
> **结论先行**:不是"只能",但在你当前的约束下,**EdgeOne 是最省事的那一个**。
> 而且真正要换的不是 CDN —— 是**「托管构建」**。EdgeOne 恰好一条顶两条(构建 + 分发),
> 这是 Cloudflare 和阿里云 ESA 都做不到的。
>
> 另外有一个**会改变结论的数字**:Gitee 免费版单仓库上限 **500MB**,而你的 `.git` 是 **588MB**。
> 所以国内代码源的第一选择不是 Gitee,而是 **CNB**。
---
## 一、把问题重新定义一次
你说「之前是 GitHub Actions 部署,用得很稳,但账号被标记了」。
那么现在这一整套东西,本质是**你自己复刻了一套 GitHub Actions**:
| GitHub Actions 原来的角色 | 你现在用自建件替代 |
|---|---|
| github.com(代码托管) | 自建 Gitea |
| GitHub 的 runner(构建执行) | act_runner |
| `upload-artifact`(产物传递) | Gitea artifact → COS → 中转机 |
| Pages 直连(产物落地) | sync.sh → 又拍云 → 多吉云 |
**所以要"简化回去",正确的问题是:找一个托管的构建服务,把这四个自建件一起替掉。**
CDN 选谁(EdgeOne / CF / ESA / 多吉云)是**另一个问题**,它不解决构建复杂度。
---
## 二、三条硬约束(都核实过,不是印象)
| 约束 | 实测/官方值 | 影响 |
|---|---|---|
| 你的仓库体积 | `.git` **588MB** / 955 提交;工作区 803MB | 决定代码源能不能装下 |
| 你的产物体积 | `public` 424MB / **3267 文件**,最大单文件 12.8MB | 决定托管平台的文件数/单文件门槛 |
| Hugo 版本 | 0.128.2 | 托管构建需能指定版本 |
| 备案 | **鄂ICP备2022015735号-1** | 境内加速的前提,你**已具备** |
---
## 三、★ 三个关键发现
### 发现 1:EdgeOne Pages **官方支持 Gitee / GitLab 作为 Git 源**
官方文档原文:*「Pages 目前支持 GitHub, GitLab, Gitee 等 Git 提供商的接入」*。
第三方 Nitro 部署文档也写:*「EdgeOne supports deployments from GitHub, GitLab, Gitee, and CNB」*。
**这条直接决定了「能不能找回 GitHub Actions 那种简单感」—— 能。**
代码推到国内托管平台 → EdgeOne 自动拉取、自动构建、自动部署。**不需要你自己的 runner。**
### 发现 2:Gitee 免费版装不下你的仓库
Gitee 官方配额页(help.gitee.com/account/usage-quota):
| 项 | 社区版免费额度 |
|---|---|
| 单仓库容量 | **≤ 500 MB** |
| 单文件 | ≤ 50 MB |
| 用户总仓库容量 | 5 GB |
**你的 `.git` 是 588MB → 超了 17.6%。** 推上去会被锁定推拉服务。
(Gitee 提供 `git-repo-clean` 瘦身工具可以降到 500MB 以下,但那是额外工作。)
### 发现 3:CNB 是更合适的国内代码源 —— 而且和 EdgeOne 有官方集成
**CNB(Cloud Native Build,cnb.cool)** 是腾讯的 AI Native Git 平台,社区版免费:
| 项 | 免费额度 |
|---|---|
| 仓库存储 | **100 GiB**(你的 588MB 毫无压力) |
| 对象存储 | 100 GiB |
| 云原生构建 | **160 核时/月** |
| 云原生开发 | 1600 核时/月 |
| 登录方式 | 微信扫码 |
**160 核时换算**:4 核跑 10 分钟 = 4 × (10/60) ≈ 0.67 核时 → 一个月能跑 **约 240 次构建**。你今天按每月 60–90 次算,**用掉不到 40%**。
而且 EdgeOne 官方专门写了 **CNB 插件文档**(`pages.edgeone.ai/document/using-cnb-plugin`):
> 在 CNB 流水线里 `npm run build` → `npx edgeone pages deploy -n <项目名> -t $EDGEONE_API_TOKEN`
**这条路官方背书,100% 可行。**
---
## 四、四种组合对比
| | 代码源 | 构建在哪 | 产物去向 | 自维护机器 | 免费额度 | 卡点 |
|---|---|---|---|---|---|---|
| **① CNB + EdgeOne Pages** ★ | CNB | **CNB 流水线**(托管) | EdgeOne 国际 + 国内 | **0 台** | 100GiB + 160 核时/月 + 500 构建/月 | 需绑微信/腾讯云账号 |
| ② Gitee + EdgeOne Pages | Gitee | EdgeOne Git 集成(4核6G) | EdgeOne 国际 + 国内 | **0 台** | 5GB / 500 构建/月 | **仓库必须先瘦身到 500MB 以下** |
| ③ GitLab.com + CF Pages | GitLab.com | CF 侧 | **仅 CF**(产物拿不出来) | 0 台 | CF 500 次/月 | 境内无解;CF 国内访问慢 3 倍 |
| ④ 保留 Gitea + EdgeOne Pages | 自建 Gitea(mirror→CNB) | CNB | EdgeOne | **1 台**(Gitea) | 同 ① | 没砍干净;但写作后台零改动 |
**为什么推荐 ①**:唯一一个同时满足「代码源在国内且装得下仓库」+「构建托管」+「境内外都能落」+「免费」的组合。
---
## 五、推荐形态
```
写作侧 构建(托管) 分发(托管)
┌────────────────────┐ ┌──────────────────────────┐ ┌──────────────────────────┐
│ write-server │ │ CNB(cnb.cool) │ │ EdgeOne 国际站 Pages │
│ (本地/服务器) │ push │ 免费 100GiB / 160 核时/月 │ │ → usj.cc 境外解析 │
│ 直接写 .md + git ├─────────▶│ │ │ │
│ 只改 remote URL │ │ .cnb.yml: │──▶│ EdgeOne 国内站 Makers │
└────────────────────┘ │ hugo --minify │ │ → 备案域名 境内解析 │
│ edgeone pages deploy │ │ │
或本地 hugo 后手动 push ─────▶│ (境外 + 境内 两个项目)│ └──────────────────────────┘
└──────────────────────────┘
```
### `.cnb.yml` 骨架(复刻你现在 deploy.yml 做的事)
```yaml
main:
push:
stages:
- name: 构建 Hugo 站点
image: klakegg/hugo:0.128.2-ext
script: |
hugo --minify --gc
- name: 部署境外
image: node:20
script: |
npx edgeone pages deploy ./public \
-n hugo-blog-overseas -t $EDGEONE_TOKEN_INTL -a overseas
- name: 部署境内
image: node:20
script: |
npx edgeone pages deploy ./public \
-n hugo-blog-cn -t $EDGEONE_TOKEN_CN -a global
```
> ⚠️ `-a global`(含中国大陆)目前只在**国际站**文档里明确,且官方文档自相矛盾。
> 另一条稳妥路径是**国内站 Makers 单独建一个项目**(账号体系与国际站独立),
> 绑你的备案域名。这个需要实测一次定论。
---
## 六、砍掉 / 保留
### 砍掉(9 项)
| 砍掉 | 原因 |
|---|---|
| act_runner | CNB 托管构建取代 |
| 广州中转机 | 产物直接落在 EdgeOne,不需要中转 |
| `sync.sh` 轮询 | 同上 |
| 腾讯云 COS | 同上(砍 COS 这件事本身也随之结束) |
| Gitea artifact 链 | 那串 `v3/v4` 兼容坑一起消失 |
| 8 处 `github.server_url` 兼容分支 | 不再需要伪装成 GitHub |
| 又拍云 | EdgeOne 国内站直接 serve 境内 |
| 多吉云 | 同上(境内 CDN 层) |
| 三套通知里的两套 | 保留一套即可 |
**发布链路:7 环节 → 3 环节**(写作 → 构建 → 上线)
**自维护机器:3 台 → 0 台**
**厂商:Gitea自建 + COS + 又拍云 + 多吉云 + EdgeOne + 失效的 GitHub → CNB + EdgeOne(同为腾讯体系)**
### 保留
- **Hugo + content + themes** —— 完全不动
- **blog-admin(artalk-cf)** —— 本来就跑在 CF Workers,一行不改
- **write-server** —— **几乎不用改**,只把 `origin` 的 remote URL 指向 CNB。
CNB 是标准 git + 提供统一 HTTPS Token,`lib/git.ts` 的 `git pull --rebase` / `git push` 逻辑照常工作
---
## 七、待确认(3 条,按重要性排序)
1. **EdgeOne 免费版的流量额度**。官方「限制与配额」页**没有列「流量」这一项**;第三方说法互相矛盾(一处说 50GB/月,一处说不限量)。
按你的站点规模估算:单页 1–3MB,50GB/月 ≈ 2–5 万次浏览 —— 个人博客大概率够,
但**那个 12.8MB 的 mp4 如果被反复播放会吃掉不少**。这一条开工前必须先确认。
2. **CNB 是否可直接作为 EdgeOne Pages 的 Git 源导入**。第三方文档说支持,官方只在插件路径里明确。
不影响可行性(走插件路径必然可行),只影响配置方式。
3. **境内用国际站 `-a global` 还是国内站 Makers 独立项目**。一次实验可定论。
---
## 八、最小验证(成本极低,建议先做)
不用改任何现有东西,**一次性验证整条链路是否成立**:
1. 建一个 CNB 仓库(微信扫码,2 分钟)
2. 往里放一个最小 Hugo 站 + 上面的 `.cnb.yml`(或先放个 hello world)
3. 拿一个 EdgeOne API Token(你已经有),跑一次 push
4. 看三件事:**CNB 能不能构建** → **能不能部署到 EdgeOne** → **国内机实测访问延迟**
这一步跑通,等于确认了整条路;跑不通,损失是 20 分钟,不是 30 天。
---
*本文档基于 2026-10-04 核实的官方文档与实测数据。所有配额均引自官方页面,第三方来源已标注。*
@@ -0,0 +1,219 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 砍掉腾讯云 COS 中转层 —— 完整改造步骤
> 目标:把「build → COS → 中转机 → 又拍云 → 多吉云」这条绕路,
> 改成「build → Gitea artifact →(中转机拉取)→ 又拍云 → 多吉云」,
> 砍掉腾讯云 COS 这一层,省下 COS 存储/流量账单。
> 状态(2026-10-04 更新):
> - ✅ `deploy.yml` 已改完并**已推送**
> - ✅ 中转机 `sync.sh.new`(artifact 版)已上传到 `/opt/upyun-sync/sync.sh.new`,**尚未生效**
> - ✅ Gitea 只读 token 已写入中转机 `/opt/upyun-sync/.gitea_token`(600 root:root)
> - ❌ 链路**尚未跑通**:run #55 倒在 v4 不支持,run #56 倒在 v3 的 finalize 500
> - ⏸️ 最新修复(单文件 tar,commit `1f568bbf`)**本地已提交、待推送**
> - ⚠️ `deploy.yml` 与 `sync.sh` 必须一起生效,否则国内线路停更(见下)
---
## 零、实测踩到的两个坑(都已修,记录备用)
### 坑 1:`upload-artifact@v4` 在 Gitea 上直接失败(run #55)
```
::error::@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+
are not currently supported on GHES.
```
Gitea 被 v4 识别为 GHES,但它没实现新版 artifact API → **降级 v3**(commit `ed38f938`)。
### 坑 2:v3 传「目录」时 Gitea finalize 返回 500(run #56,耗时 30 分钟)
v3 收目录是**逐文件上传**:3216 个 blob / 376MB 全部传完(日志可见 `Processed file #3216`),
但最后的 finalize 调用被 Gitea 拒绝:
```
Finalize artifact upload - Attempt 1..5 of 5 failed with error: Request timeout
::error::Finalize artifact upload failed: Artifact service responded with 500
```
**结论:Gitea 扛不住「文件数多」的 artifact。** 改法(commit `1f568bbf`):
build 侧先把 `public/` 打成**单个** `public.tar.zst`,只上传这 1 个文件。
副作用是好的——产物结构从此完全确定,不再有「解出来会不会多一层 `public/`」的歧义。
---
## 一、为什么不能只推一半(重要)
deploy.yml 改完后,build 产物**不再上传 COS**,只进 Gitea artifact。
而国内中转机的 sync.sh 如果**还从 COS 拉**,就会:
- build 传 artifact → COS 没有新产物
- 中转机从 COS 拉 → 拿到的是旧产物 / 拉不到
- **国内线路(又拍云 + 多吉云)停更**
所以 `deploy.yml` 和 `sync.sh` 必须**同步改、同步推**,不能只推 deploy.yml。
---
## 二、已完成的改动(deploy.yml)
文件:`.github/workflows/deploy.yml`
### build job
- ❌ 删掉:「Setup Python」「Install/Configure coscmd」「Get last build hash from COS」「Check if upload is needed」「Upload to Tencent COS」「Upload hash to COS」
- ✅ 新增:「Upload build artifact (public/)」→ `actions/upload-artifact@v4`,name=`hugo-public`,path=`public/`,retention-days=7
- 保留:「Report deploy status (built)」上报 hash 到 `api.200181.xyz`
### deploy-edgeone job
- ❌ 删掉:「Check if deployment is needed」(last_hash 判断)、「Setup Python」「Install/Configure coscmd」「Download files from COS」
- ✅ 改成:`actions/download-artifact@v4`(name=`hugo-public`,path=`public/`)→ 直接 `npx edgeone pages deploy ./public`
- `deployed` 恒为 true(push 才触发,本就该部署)
### 清理
- 删掉所有 `download_duration` / `last_hash` / `need_upload` / `coscmd` 引用(通知、表格里的「下载 COS」等)
---
## 三、剩下的改动:sync.sh(国内中转机)
文件:`/opt/upyun-sync/sync.sh`(国内机 119.29.215.187,通过 1Panel API 或 SSH 操作)
### 要改的核心逻辑
原流程(第 1-3 步):
```bash
# 1. 取远端 hash:从 COS 读 /__build_hash(cos_sign.py 签名)
# 2. 比对本地 hash,无变化退出
# 3. 从 COS 下载 public.tar.zst
```
新流程:
```bash
# 1. 调 Gitea API 列出最新 artifact
# GET https://gitea.usj.cc/api/v1/repos/zqlit/blog/actions/artifacts?name=hugo-public
# (Header: Authorization: token <GITEA_TOKEN>)
# 2. 拿最新一个 artifact 的 id,下载 zip
# GET .../actions/artifacts/{id}/zip (302 重定向,curl -L 跟随)
# 3. 解压 zip → 读 public/.build_hash → 比对本地 hash
# 有变化才继续走「upx sync 又拍云 → purge → 刷多吉云」
```
### 需要的准备
1. **Gitea 只读 token**:
- Gitea 后台(gitea.usj.cc)→ 头像 → 设置 → 应用 → 生成令牌
- 名称随意(如 `upyun-sync`),勾选 `read:repository` 即可(只读够用)
- 生成后把 token 存到中转机 `/opt/upyun-sync/.gitea_token`(chmod 600)
2. 中转机访问 Gitea 的网络:已实测 0.56s,稳定,无问题。
### sync.sh 改动示例(第 1-3 步替换成如下)
```bash
# ---------- 取最新构建(从 Gitea artifact,替代原 COS)----------
GITEA="https://gitea.usj.cc"
REPO="zqlit/blog"
TOKEN=$(cat "$ROOT/.gitea_token" 2>/dev/null)
# 1. 列出 hugo-public artifact,拿最新 id + created_at
ART_LIST=$(curl -fsS --max-time 40 -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/actions/artifacts?name=hugo-public" 2>/dev/null)
ART_ID=$(echo "$ART_LIST" | python3 -c "import sys,json; a=json.load(sys.stdin); print(a[-1]['id'] if a else '')")
[ -n "$ART_ID" ] || { say "❌ 没有找到 hugo-public artifact"; exit 1; }
# 2. 下载 artifact zip
rm -f "$ROOT/artifact.zip"
curl -fSL --max-time 600 -H "Authorization: token $TOKEN" \
"$GITEA/api/v1/repos/$REPO/actions/artifacts/$ART_ID/zip" \
-o "$ROOT/artifact.zip" || { say "❌ 下载 artifact 失败"; exit 1; }
# 3. 解压(artifact zip 里是 public/ 的内容)
rm -rf "$ROOT/public.new"; mkdir -p "$ROOT/public.new"
unzip -q "$ROOT/artifact.zip" -d "$ROOT/public.new" || { say "❌ 解压失败"; exit 1; }
# 4. 读 hash 比对(.build_hash 在 zip 里 public/.build_hash)
REMOTE=$(cat "$ROOT/public.new/.build_hash" 2>/dev/null | tr -d ' \r\n')
LOCAL=$(cat "$STAMP" 2>/dev/null | tr -d ' \r\n')
if [ "$REMOTE" = "$LOCAL" ] && [ -f "$PUBLIC/index.html" ]; then
say "无变化 ${REMOTE:0:12}…"; exit 0
fi
say "发现新构建: ${LOCAL:0:12}… -> ${REMOTE:0:12}…"
# 后续:原子切换 public.new → public,然后照旧 upx sync / purge / 刷多吉云
# (这部分逻辑不变,只是"下载产物"和"取 hash"的来源从 COS 换成了 artifact)
```
> ⚠️ 注意:artifact 解压出来的目录结构取决于 `upload-artifact` 的 path。
> deploy.yml 里 `path: public/`,所以 zip 里是 `public/...`,解压后要取 `public.new/public/`
> 或调整 path。实测一次 build 看 zip 结构最稳。
---
## 四、执行顺序(照着做)
### 第 1 步:生成 Gitea token
Gitea 后台 → 设置 → 应用 → 生成令牌 → 只读 → 复制 token
### 第 2 步:token 放到中转机
```bash
# 通过 1Panel API 或 SSH(国内机)
echo "你的token" > /opt/upyun-sync/.gitea_token
chmod 600 /opt/upyun-sync/.gitea_token
```
### 第 3 步:改 sync.sh
按上面「三」的示例,把「取 hash + 下载」两段从 COS 换成 Gitea artifact。
### 第 4 步:推 deploy.yml
```bash
cd /e/GitHub/blog
git add .github/workflows/deploy.yml
git commit -m "chore: 砍掉 COS 中转,build 产物改用 Gitea artifact 传递"
git push gitea main
```
### 第 5 步:验证
1. 看 Gitea Actions 这次 build 是否成功(重点看 `upload-artifact` 有没有报错——这是 Gitea 28 兼容性风险点)
2. 看中转机 sync.log 有没有「✅ 同步完成」
3. 打开 `https://api.200181.xyz/api/deploy-status` 看国内线路是否 synced
4. 访问 usj.cc 看国内是否更新
### 第 6 步:确认稳定后,清理 COS
- 删 Gitea secrets 里的 COS_SECRET_ID / COS_SECRET_KEY / COS_BUCKET / COS_REGION(先留着观察几天再删)
- 腾讯云 COS 桶 `hugo-1303964578` 可以清空或删桶(确认不再被引用后)
- 中转机的 `cos_sign.py` 可以删
---
## 五、风险点与回退
| 风险 | 说明 | 对策 |
|---|---|---|
| `upload-artifact@v4` 在 Gitea 28 兼容性 | v4 是新版 action,Gitea 兼容层可能只支持 v3 | 若报错,降级到 `actions/upload-artifact@v3` |
| artifact 保留期 7 天 | 若长时间不部署,artifact 过期 | retention-days 设大,或确认 deploy-status 兜底 |
| zip 目录结构 | `path: public/` 解压后是 `public/...` 还是直接文件 | 实测一次 build 看 zip 结构再定解压路径 |
| 回退 | 万一 artifact 链路不通 | 原 deploy.yml 备份在 `.workbuddy-backup/deploy.yml.20261004-085652`,`git revert` 即可回 COS |
---
## 六、原始备份位置
- deploy.yml 原始版本:`E:/GitHub/blog/.workbuddy-backup/deploy.yml.20261004-085652`
- 已改版本:`E:/GitHub/blog/.github/workflows/deploy.yml`(未推送)
---
## 七、关键信息速查
| 项 | 值 |
|---|---|
| 境外 Gitea | `23.254.236.47:3001`(域名 gitea.usj.cc),SSH root / `5I3fXqbV5Aco9aW9K9` |
| Gitea 数据 | docker `gitea/gitea:latest`,`/opt/gitea/data`,SQLite |
| 国内中转机 | `119.29.215.187:3721`(1Panel),又拍云同步任务 ID=15 |
| 中转机 sync.sh | `/opt/upyun-sync/sync.sh` |
| Gitea artifact API | `GET /api/v1/repos/zqlit/blog/actions/artifacts` 和 `/{id}/zip` |
| 又拍云 | bucket=`imzql`,operator=`1770186415` |
| 多吉云 | AK=`4fa23701981899fb`(sync.sh 内已硬编码) |
@@ -0,0 +1,231 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 精简方案:只留 Cloudflare + Hugo
> 2026-10-04
> 目标:砍掉全部自建基础设施,只保留 **Hugo**(构建)和 **Cloudflare**(一切服务端)。
> 文中所有平台限制均来自官方文档核实,非推测。
---
## 一、决定一切的硬约束
**Cloudflare 唯一不能替你做的是「代码托管」。** 它的两条部署路线直接决定架构形态:
| 模式 | 代码源 | 构建在哪 | 适用场景 |
|---|---|---|---|
| **Git 集成** | **只认 github.com / gitlab.com**<br>(官方原文:*does not currently support connecting self-hosted instances*) | Cloudflare 侧自动构建 | 要「push 即上线」 |
| **Direct Upload** | 不需要 git | 不构建,你本地构建好推产物 | 要「零第三方依赖」 |
> ⚠️ **两种模式在项目创建时定死,事后不能互转**(官方明说)。选错只能重建项目 —— 这是最容易踩的坑。
> 变通办法:建 **Git 集成**项目,之后在 `Settings → Builds` 关掉「自动部署」,再用 `wrangler` 直传 —— 这样两种方式都能用(**反过来不行**)。
### 关键结论
**GitHub 被封 ≠ 必须自建 Gitea。** 你不需要「自建 git 托管」这种复杂度,只需要回答一个问题:
> **你要不要「不开电脑也能发文」?**
---
## 二、两条路线
### 形态 A · 本地构建直传(最简,零第三方)
```
你的电脑 Cloudflare
┌─────────────────────┐ ┌──────────────┐
│ 写 md → hugo build │──wrangler─▶│ Pages (静态站) │
└─────────────────────┘ pages ├──────────────┤
deploy │ Workers (评论/RSS) │
└──────────────┘
```
- **组件数**:1 台自己的电脑 + Cloudflare
- **第三方依赖**:0(连 git 托管都不要)
- **发布链路**:3 步,全在本机
- 代码备份:本地仓库即可,要冗余可推任意 git,或存 CF R2
- **代价**:发文必须开电脑
### 形态 B · GitLab.com 当代码源 + CF 自动构建
```
写 md ──push──▶ gitlab.com ──自动构建──▶ Cloudflare Pages ──▶ 上线
Cloudflare Workers (评论/RSS)
```
- **组件数**:gitlab.com + Cloudflare
- **★ 关键**:构建跑在 **Cloudflare 侧**(免费 500 次/月、单次 20 分钟超时),**不消耗 GitLab 的 400 分钟**
→ 所以之前担心的「GitLab 免费版 400 分钟/月不够用」在这套组合下**根本不成立**
- 额度核对:本站 3267 文件 / 最大单文件 12.8MB → CF 上限 20,000 文件 / 25MiB ✅ 完全够
- **代价**:多一个外部账号
---
## 三、可以整个删掉的(9 项)
| # | 删掉的 | 原本的作用 |
|---|---|---|
| 1 | **Gitea** | 自建 git 托管(GitHub 被封后的替代品) |
| 2 | **act_runner** | 自建 CI runner |
| 3 | **广州中转机 + `sync.sh`** | 每分钟轮询、把产物搬去又拍云 |
| 4 | **又拍云** | 国内源站 |
| 5 | **多吉云** | 国内 CDN,回源又拍云 |
| 6 | **EdgeOne Pages** | 境外线路托管 |
| 7 | **腾讯云 COS** | 境外产物 → 国内的中转层 |
| 8 | **artifact 传递链** | 跨 job 传 424MB(含 v3/v4 那串坑) |
| 9 | **兼容分支与冗余通知** | `github.server_url` 判断 ×8、TG/飞书/邮件三套、`if:false` 死代码约 100 行 |
**发布链路从 7 环节降到 3 环节;需要自己运维的机器从 3 台降到 0 台。**
---
## 四、Cloudflare 侧能白捡留下的
**`blog-admin/`(artalk-cf 评论后端 + RSS 机器人)本来就是 CF Workers + D1 + KV —— 一行都不用改。**
这是你现有架构里唯一「已经完全符合目标形态」的部分:
- 评论系统 → `api.200181.xyz`,照常
- RSS 订阅抓取、友链朋友圈数据 → 照常
- 后台管理(`/admin/` 与 `/sidebar/`)→ 照常
**换句话说:你的「后端」其实早就已经是 Cloudflare 了,复杂的只是「发布管线」。**
---
## 五、必须接受的代价(诚实说)
### 1. 国内访问会变慢 ← 最大的一笔
Cloudflare 免费版在中国大陆走**境外节点**(没有 ICP 备案就用不了大陆节点,China Network 要企业版)。
现在这套「又拍云 + 多吉云」的存在意义**就是为了这个**。
**2026-10-04 国内机实测**(腾讯云广州,见「五·补」章):CF 首字节 **0.61–0.89s**,
而现在的国内 CDN 是 **0.18–0.32s** —— 即 **CF 比国内 CDN 慢约 2–4 倍**。
能开、不算快,对个人博客通常可接受,但你要心里有数。
### 2. 写作后台(`post.usj.cc`)会没有
那套 Next.js 直接读写本地 `.md` 文件、依赖文件系统,**CF 上跑不了**。要么:
- 重写成 CF Worker(内容改存 R2/D1,图片存 R2)—— 你会写 Worker,技术可行,但是工作量
- 退回本地写作(配合形态 A 正好)
### 3. Telegram bot 发文、公众号发布会断
同理,需要重写成 CF Worker 才能保留。
### 4. 构建前的脚本要处理
`check_links.js`(友链检查)、`generate_circle_data.js`(朋友圈数据)、`add_draft_to_hidden.py` 这些:
- 要么塞进 CF Pages 的构建命令里(需要 `npm ci`,会拖慢构建、且 CF 构建环境要能装依赖)
- 要么去掉(友情链接检查其实没必要每次构建都做)
---
## 五·补、国内访问能不能救?(2026-10-04 国内机实测)
用国内那台机器(腾讯云广州,`119.29.215.187`)实测,每项 3 次取代表值:
| 目标 | 首字节 TTFB | 连接建立 | 走哪个节点 |
|---|---|---|---|
| `usj.cc`(现状:EdgeOne **国内**节点) | **0.18–0.32s** | 0.01–0.04s | 成都 / 浙江 |
| `api.200181.xyz`(你自己的 CF Workers) | **0.61–0.89s** | 0.20–0.45s | CF Anycast(IPv6) |
| `cravatar.cn`(头像是 CF 的) | 0.65–0.71s | 0.23s | CF Anycast |
| `spst2.com`(页首统计脚本) | 0.087s | 0.011s | 有国内节点,**不是问题** |
**两条硬结论:**
1. **CF 免费版在国内比国内 CDN 慢约 2–4 倍**(TTFB 0.6–0.9s vs 0.2–0.3s)。
2. **实测发现 CF 的连接走了 IPv6**(`2606:4700::…`),握手要 0.20–0.45s;
同一时刻 IPv4 握手只要 **0.058s**。→ **IPv6 到 CF 的路由在国内明显更差**,
这是一个可以单独利用的抓手。
### 能加速的三条路
**路 A · 官方(走不通)**
Cloudflare China Network 需要:Enterprise 企业版 + 单独订阅 + **每个主域名持有效 ICP 备案** +
京东云内容审核。个人博客没戏。
**路 B · 社区「优选 IP」(有效,但违反 ToS)**
原理:CF 用 Anycast 广播,大陆解析到的 IP 段路由差。做法是**让大陆用户的 DNS 解析结果
指向另一段「对大陆友好」的 CF IP**。
- 预期效果:按本机实测基线,合理预期是 **0.6s → 0.2–0.3s**(社区常说的「5s→2s」是针对更差的默认线)
- ⚠️ **Pages 不能直接改 CNAME**(会返回 `1001`)→ 必须「转成 Worker」或「交给第三方 DNS 分线路解析」
- ⚠️ **明文违反 CF 服务条款 2.2.1(b)**:*causing traffic for your Cloudflare-proxied domain
to be sent to an IP address that was not assigned by Cloudflare for the domain*。
官方保留封号权;社区共识是「用别怕,怕别用」,建议**账号隔离**(主站一个号,优选另开小号)
- ⚠️ **要维护**:优选 IP/域名会失效,得定期更换;个别优选域名还会被地方屏蔽
**路 C · 不违规的优化(零风险,先做)**
- ✅ **已做**:字体自托管(`/font/zql-v2.woff2`,无 Google Fonts)、CSS 合并成单文件、图片 WebP
- 头像源 `cravatar.cn` 走 CF(0.65s)→ 可换国内源(站内已有 `q1.qlogo.cn` 这类)
- 加 **Cache Rules** 长缓存 + **Early Hints** + **Tiered Cache**(免费可用)→ 改善重复访问
- 若走第三方 DNS(路 B 顺带),**只下发 A 记录、不下发 AAAA** → 直接规避上面那条差的 IPv6 路由
### 一句话结论
**你自己的实测就是答案:国内 CDN 快 2–4 倍,而且完全合规。**
CF 免费版在国内的「加速」本质是**可用的灰色技巧 + 持续维护成本**。
- 如果「只留 CF」是硬目标 → 走 **Worker + 第三方 DNS 只给 IPv4**(比优选 IP 干净,且顺带修 IPv6 问题)
- 如果真正的目标是「砍复杂度」→ 记住:**复杂度的大头在「构建与搬运」**(Gitea / act_runner / 中转机 / COS / artifact),
**不在 CDN 的数量**。多留一个国内 CDN 不会让架构变复杂;砍掉那串搬运链才会。
---
## 六、我的建议
**先回答那个问题:需不需要「不开电脑也能发文」?**
| 你的答案 | 选 | 理由 |
|---|---|---|
| **不需要**(本来就在电脑上写) | **形态 A** | 真正的「只要 CF + Hugo」:0 台服务器、0 个第三方托管 |
| **需要**(手机/TG 发文) | **形态 B** | GitLab 只当「代码网盘 + 触发器」,构建和托管全在 CF |
**两条都不要再去自建 Gitea** —— 那正是你想摆脱的复杂度。
**一个必须提前想清楚的点**:CF Pages 的 Git 集成与 Direct Upload **不能互转**,项目创建时就要定。
如果不确定,**先建 Git 集成项目、之后关掉自动部署用 wrangler 直传**,把两条路都留着。
---
## 七、如果还想更彻底(建议不要)
把内容也搬进 CF:文章存 R2/D1,写作后台写成 CF Worker。但 ——
**Hugo 构建需要文件系统 + 执行二进制,跑不进 Worker。** 所以要么放弃 Hugo(改成运行时渲染),要么保留一个构建执行者(那就回到形态 B 了)。**复杂度会绕回来,不建议。**
---
## 附 A:落地顺序(以形态 B 为例)
1. GitLab.com 建**私有**项目 → 现有仓库推上去(`.git` 588MB,首次跨境推送需要时间)
2. CF Pages 新建项目 → **Connect to Git → 选 GitLab**
- 构建命令:`hugo --minify`
- 输出目录:`public`
- 环境变量:`HUGO_VERSION=0.128.2`(CF 构建镜像默认是 0.147.7,要锁到你的版本)
3. 绑定自定义域名 `usj.cc`
4. `blog-admin` 保持不动(本来就在 CF)
5. 停掉 Gitea / act_runner / 中转机的同步计划任务;DNS 切到 Cloudflare
6. **观察国内访问速度** —— 这是唯一的实质性风险点
## 附 B:形态 A 的落地顺序
1. 本机装 `hugo` 0.128.2 + `wrangler`(Node 环境已有)
2. CF Pages 新建 **Direct Upload** 项目(**注意:一旦选它,以后不能改成 Git 集成**)
3. 本机 `hugo --minify && npx wrangler pages deploy public --project-name=<项目名>`
4. 绑定域名,完成
5. 其余全部关停
---
## 附 C:本方案的证据出处
- Cloudflare 官方文档 *Git integration*:「Pages offers support for GitHub and GitLab」「does not currently support connecting self-hosted instances of GitHub or GitLab」
- 同上:「You cannot switch to Direct Upload later」
- Cloudflare 官方文档 *Direct Upload*:Wrangler 上传上限 **20,000 文件 / 25 MiB**;拖拽上限 1,000 文件
- Cloudflare 官方文档 *Workers Billing and Limitations*:静态资源请求**免费且不限量**,20,000 文件 / 25 MiB
- Cloudflare Blog:*Cloudflare Pages 现已提供对 GitLab 的支持*
- CF Pages 免费额度:500 次构建/月、1 并发、单次 20 分钟超时
- 本站实测:`public/` = 424MB / 3267 文件,最大单文件 12.8MB
@@ -0,0 +1,172 @@
> 📦 **本文档已归档**(2026-10-06)。它记录的是**当时的评估与方案**,其中的结论可能已被后续决策推翻。
> **请勿据此判断当前架构** —— 现行架构唯一事实源是 [`架构总览.md`](../../../架构总览.md)。
# 阿里云 ESA 评估(对比 EdgeOne)
> 2026-10-04 · 问题:「阿里云也有个跟 EdgeOne 很像的服务,能不能考虑一下?」
> 现状:`usj.cc` 域名 **境外解析在 EdgeOne、境内解析在多吉云**(DNS 分线路)
## 一、结论先说
**能考虑,但对你这个站有一条硬伤,直接卡住:ESA Pages 每个项目最多 2000 个文件,你的产物是 3267 个。**
而 EdgeOne Pages 是 **20000 个**——你只用了 16%。
所以结论是:**换阿里云 ESA 不是"简化",是"降级到刚好装不下"。**
---
## 二、阿里云对标的到底是什么
| 腾讯云 | 阿里云 |
|---|---|
| EdgeOne(边缘安全加速平台) | **ESA(Edge Security Acceleration)** —— 就是原来的 **DCDN 改名** |
| EdgeOne Pages(Git 导入 + 自动构建 + 静态托管) | **ESA Pages**(也叫「函数和 Pages」) |
| Edge Functions / Cloud Functions | **ESA 边缘函数**(V8 Isolate,只支持 JS) |
| 节点 | 阿里云 3200+ 边缘节点 |
**能力确实是同级的**——都免费、都支持 ICP 备案、都能绑备案域名走大陆节点、都是「Git 导入 + 自动构建 + 全球分发」一套。你问得没错,这是对标产品。
**但两者的尺寸门槛差了 10 倍,这是分水岭。**
---
## 三、★ 决定性的一张表:文件数门槛
我逐个查了官方配额页(不是二手说法):
| 项目 | **ESA Pages** | **EdgeOne Pages** | Cloudflare Pages |
|---|---|---|---|
| **单项目文件数** | **2000** ❌ | **20000** ✅ | 20000 ✅ |
| 单文件大小 | 25 MB | 25 MB | 25 MiB |
| 源码包大小 | 1024 MB | — | — |
| 总存储容量 | — | 5 GB | — |
| 构建次数 | — | 500 / 月 | 500 / 月 |
| 构建超时 | — | 20 分钟 | 20 分钟 |
| 构建算力 | — | **4 核 6 GB** | — |
| 自定义域名 | — | 200 个 | — |
| **你的站** | **3267 → 超限 63%** | 3267 → 占 16% | 占 16% |
来源:阿里云官方文档《函数和 Pages 使用限制》——*"Pages 文件数 2000 个:每个 Pages 项目最多可上传 2000 个静态文件"*;EdgeOne 官方《限制与配额》——*"单项目文件数 20000"*。
**这一条把 ESA 从候选名单里筛掉了,其他指标都不用比了。**
> 附带发现:**EdgeOne Pages 的构建环境是 4 核 6 GB** —— 比你那台广州中转机(2 核 1.9 G)强得多。如果走它的 Git 集成自动构建,hugo 构建完全不需要你自己的机器。
---
## 四、你的 3267 个文件是怎么构成的
```
1337 html ← 文章页 + 分类/标签/归档页
1211 png ┐
295 webp │
254 jpg ├─ 图片/视频类合计 1778 个(占 54%)
7 mp4 │
6 jpeg │
3 svg ┘
66 xml ← sitemap / rss
59 js
3 woff2 / 3 woff / 3 mp3 / 3 css / 6 json / 2 txt
────────────────
3267 总计
```
**纯文本类(html/xml/css/js/json)只有 1473 个** —— 这个数字很关键,它意味着「如果非要用 ESA」还有一条窄路(见第六章)。
---
## 五、ESA vs EdgeOne:真实差异(去掉重复项之后)
免费套餐**大部分能力是重复的**,真正有差别的是这几条:
| 维度 | ESA | EdgeOne | 谁赢 |
|---|---|---|---|
| **文件数** | 2000 | **20000** | **EdgeOne(决定性)** |
| 大陆节点 | 有(需备案) | 有(需备案) | 平 |
| 流量 / 请求 | 不限 | 不限 | 平 |
| **开通大陆加速的门槛** | ⚠️ **默认不含大陆,要发帖解锁** | 直接可用 | **EdgeOne** |
| 免费套餐数量 | 每账号限 **1 个** | 站点 1 主域 + 200 子域 | EdgeOne |
| 构建算力 | 未披露 | 4 核 6 GB | EdgeOne(已知值) |
| 单文件限速 | 官方未披露;社区称"不限速",另有称峰值 5 Mbps —— **存疑** | 单文件单线程 4 Mbps(≈500 KB/s) | **ESA 可能赢** |
| WebSocket | 免费版**不支持**(第三方实测) | — | EdgeOne |
| 速度(第三方实测) | 大陆节点覆盖**更多**、网络速度**更高** | 加速效果更佳、全球覆盖更广(Anycast) | 各有说法 |
| 迁移成本 | 要重接一次 | **你已经在用** | **EdgeOne** |
### 值得注意的两点
1. **ESA 免费版默认不含中国大陆加速** —— 官方原文:*"By default, this plan does not include Chinese mainland acceleration."* 要解锁得**在任意社交/博客平台发一篇 30 字以上的推荐帖**(带 `#AlibabaCloudESA #ESAPages` 标签 + 官方图),再进群提交链接。你写博客顺手能完成,但这是个**额外操作**,跟"简化"反向。
2. **ESA 免费版的「不限速」有矛盾说法** —— 一方说无限速适合大流量,一方说峰值带宽约 5 Mbps。如果你的痛点是那个 12.8 MB 的 mp4(EdgeOne 下首载约 25 秒),这一条**值得实测**,但它是 ESA 唯一可能赢的地方。
---
## 六、如果非要用 ESA:唯一的一条路
把 **1778 个图片/视频剥离到对象存储**(OSS / 又拍云 / 多吉云存储),Pages 只托管 1473 个文本文件 → 落回 2000 以内。
**但代价是**:多一个存储服务、多一套域名与缓存配置、构建产物要改写图片链接。**这是"增加复杂度"换"换一家厂商",跟你的目标正好相反。**
不建议。
---
## 七、ESA 唯一值得拿的东西(互补用法)
**如果只是想用阿里云,不要用 ESA Pages,用 ESA 的 CDN 加速能力。**
ESA 免费版(Entrance plan)本质是**一个不限流量的 CDN**,可以回源到任意源站。它能做的是:
- 回源到你的又拍云 / OSS 存储 → 只加速,不托管,**没有 2000 文件限制**
- 用 ESA 的**边缘函数**收编现在的 `blog-admin`(CF Workers)
这确实有个价值:**阿里云一家能同时给你 CDN + Pages + 边缘函数 + 对象存储**,比「EdgeOne + CF Workers + 又拍云 + 多吉云」更统一。
但这又引入了**第四家厂商**,而你现在的问题恰恰是厂商太多。
---
## 八、而且你现在的双线路,其实已经是"简化过"的形态
你说的「境外 EdgeOne、境内多吉云」,本身就是**分线路 DNS 调度**的结果:
```
usj.cc
├─ 境外线路 → EdgeOne(Pages 项目,--area overseas)
└─ 境内线路 → 多吉云 CDN(融合 CDN,70+ 节点,20 GB/月免费)
```
**这已经做到了"境内外分流"** —— 跟 EdgeOne 双区域方案想达成的效果是一样的。所以真正的问题不是「CDN 选阿里云还是腾讯云」,而是:
> **要不要把这两条线合并成一家?**
- **合并到 EdgeOne**:改一个参数(`-a global`)或建国内站项目 → 砍掉多吉云 ✅
- **合并到 ESA**:从零接一次,还撞上 2000 文件上限 ❌
- **维持现状**:多一条线,但**不增加架构复杂度**——CDN 数量不等于复杂度
**注意最后一条**:你架构的复杂度大头在**构建与搬运**(Gitea、act_runner、中转机、COS、artifact 链),不在 CDN 数量。多留一个多吉云,不会让架构变复杂;砍掉那条搬运链才会。
---
## 九、建议
1. **别换 ESA Pages** —— 2000 文件上限是硬伤,你的站 3267,装不下。
2. **想收敛到一家,就走 EdgeOne** —— 你已经在用,同一个控制台、同一个项目(`-a global` 实验),或者建个国内站项目。迁移成本近零。
3. **多吉云可以留着** —— 它免费 20 GB/月、融合 CDN(依托大厂节点)、速度好,而且它是**独立的国内线路**,跟 EdgeOne 互为备份。不构成复杂度问题。
4. **如果哪天站点瘦身到 2000 文件以内**(比如把 1778 个图片全挪到对象存储),ESA 会重新变成候选 —— 但那时 EdgeOne 也一样能用,还是没必要换。
---
## 十、待核实项(不装作确定)
| # | 事项 | 状态 | 怎么验 |
|---|---|---|---|
| 1 | 多吉云当前的回源源站是谁(又拍云存储 / EdgeOne / 其他) | **待确认** | 多吉云控制台看回源配置 |
| 2 | ESA 免费版是否真的"不限速"(vs 峰值 5 Mbps) | **说法矛盾** | 需实测,但没必要为它迁移 |
| 3 | ESA Pages 是否支持 Gitee 作为代码源 | 官方只列 GitHub,第三方提到 Gitee | 若成立,对简化链路有价值 |
| 4 | EdgeOne Pages 的 Git 集成是否支持 Gitee | 上次标的第 5 条,仍未确认 | 若支持可砍掉自建 Gitea + act_runner |
---
## 十一、一句话总结
**阿里云 ESA 确实是对标 EdgeOne 的产品,能力同级——但 ESA Pages 限 2000 文件,你的站 3267 文件,直接在门口就被拦下。**
**换厂商解决不了你的问题;你的问题在构建与搬运链,不在 CDN 选谁。**