1035 字
5 分钟
封面与画廊规则详解
Xenia
2025-11-28
0 次
0 人

一、背景#

博客最初设计时,文章详情页会自动提取文章第一张图片作为封面。但这种隐式逻辑存在以下问题:

  1. 无法手动指定喜欢的图片作为封面
  2. 画廊页面无法显示文章(因为没有显式的 image 字段)
  3. 推送 GitHub 时,部分空 image 字段导致构建失败

二、解决方案#

2.1 BAT 脚本自动提取#

修改 scripts/add-frontmatter.cjs,实现以下逻辑:

场景处理方式
完全没有 frontmatter添加完整模板,自动提取第一张图片
已有 frontmatter 但无 image补充 image 字段,提取第一张图片
文章无图片添加空的 image 字段
已有 image 字段跳过,不重复处理

2.2 配置文件修复#

修改 src/content/config.ts,允许 image 为空:

image: z.string().optional().default("").nullable(),

关键点:.nullable() 让空字符串也能通过验证,不再导致构建失败。

三、画廊实现#

3.1 页面结构#

/gallery/ → src/pages/gallery/index.astro
→ src/components/GalleryPanel.astro

3.2 显示逻辑#

画廊页面只显示显式设置了 image 字段的文章:

const postsWithCover = posts.filter(post => post.data.image);

3.3 文章详情页回退#

如果没有设置 image,文章详情页会自动提取第一张图片:

PostPage.astro
function extractFirstImageFromMarkdown(markdown) {
const imgMatch = markdown.match(/!\[.*?\]\(([^)]+)\)/i);
return imgMatch ? imgMatch[1] : undefined;
}

四、使用流程#

4.1 写文章#

  1. 在 Obsidian 中新建 md 文件
  2. 写入文章内容(建议在开头插入一张图片)
  3. 保存文件

4.2 运行 BAT 脚本#

  1. 打开 博客升级版bat.bat
  2. 选择 选项2(生成 YAML 模板)
  3. 脚本会自动:
    • 添加/补全 frontmatter
    • 提取文章第一张图片作为封面
    • 在控制台显示处理结果

4.3 查看效果#

⚠️ 注意:Obsidian 有缓存,修改后需要:

  • Ctrl+S 保存
  • 关闭文件重新打开
  • 或刷新编辑器

五、注意事项#

5.1 图片格式#

必须是 Markdown 图片格式:

![说明](https://example.com/image.jpg)

5.2 推送问题#

修复前:空 image 导致构建失败
修复后:.nullable() 允许空值,构建正常

5.3 画廊显示#

  • 有 image 字段 → 画廊显示 ✅
  • 无 image 字段 → 画廊不显示,但文章页会自动提取第一张图 ✅

六、相关文件路径#

功能文件路径
脚本scripts/add-frontmatter.cjs
配置src/content/config.ts
画廊组件src/components/GalleryPanel.astro
画廊页面src/pages/gallery/index.astro
封面提取src/components/PostPage.astro

七、攻坚过程复盘(2026-04-22 凌晨)#

7.1 问题背景#

从今天早上开始,我们持续攻坚封面自动提取功能,历经多次挫折才最终解决。

7.2 遇到的困难#

阶段问题后果
初期Obsidian 缓存导致看不到变化误以为脚本不生效
中期image: 导致构建失败EdgeOne 部署报错
后期图片提取逻辑错误提取了 frontmatter 中的图片而非正文

7.3 失败原因分析#

  1. Obsidian 缓存问题

    • 现象:脚本运行后,Obsidian 显示的文件内容没有变化
    • 原因:Obsidian 有文件缓存,需要手动刷新或关闭重开
    • 解决:增加提示信息,提醒用户刷新
  2. 构建失败问题

    • 现象:image: Expected type "string", received "null"
    • 原因:空 image: 字段被解析为 null,而不是空字符串
    • 解决:在 config.ts 中添加 .nullable()
  3. 图片提取错误

    • 现象:提取的是 frontmatter 中的图片,不是文章正文的第一张图
    • 原因:extractFirstImage() 函数直接匹配整个文件内容,没有忽略 frontmatter
    • 解决:新增 extractFirstImageIgnoreFrontmatter() 函数,先解析出文章正文再提取图片

7.4 最终突破#

// 正确的提取逻辑
function extractFirstImageIgnoreFrontmatter(content) {
// 1. 找到 frontmatter 结束的位置
const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/m);
if (match && match[1]) {
// 2. 只从正文内容中提取图片
const bodyContent = match[1];
return extractFirstImage(bodyContent);
}
// 3. 如果没有 frontmatter,直接提取
return extractFirstImage(content);
}

7.5 经验总结#

  • 调试时注意缓存:IDE/编辑器的缓存可能导致误判
  • Zod 验证需考虑空值.nullable() 可以解决空字符串验证问题
  • 正则匹配要精确:提取内容时需明确范围,避免误提取

八、验证方法#

  1. 新建测试文章,包含图片
  2. 运行 BAT 脚本选项2
  3. 检查文件是否添加 image: 字段
  4. 访问 /gallery/ 查看是否显示
  5. 构建测试:pnpm build

这篇文章是否对你有帮助?

发现错误或想要改进这篇文章?

在 GitHub 上编辑此页