记录个人博客中图像引用方式的一次集中整理。
这次调整统一文章了中的图片组件写法、去掉硬编码路径、把若干旧资源切换到 WebP。
一、概要
相关提交参见feat: 优化图片引用
统一博客里的图片组件、取消硬编码路径和不一致的资源格式。

调整范围包括:
public/*(图片添加与路径规范化).gitignore(排除非WebP文件).vitepress/config.mts(优化vite引用alias)collections/*(修改文集封面)posts/*(修改文章图像引用)
二、原始方案问题
1. 图像样式不成组件
在之前的文章中,图像要么使用Markdown原生图片[](..),要么使用<img>标签,样式极其混乱,而且图像排版也很难看。
例如
html
<div class="media-grid"> <article class="media-card"> <img src="/Imgs/operating-sys-synchronize/单一实例死锁.png" alt="单一实例死锁"/> <h3 class="media-title">单一实例包含环死锁</h3> </article> <article class="media-card"> <img src="/Imgs/operating-sys-synchronize/多实例死锁.png" alt="多实例死锁"/> <h3 class="media-title">多实例包含环死锁</h3> </article> <article class="media-card"> <img src="/Imgs/operating-sys-synchronize/多实例未死锁.png" alt="多实例未死锁"/> <h3 class="media-title">多实例包含环但未死锁</h3> </article></div>使用CSS类名将样式应用到图片上,无论是在样式管理还是图片本身路径管理上都是潜在的石山生成器。
2. 图片路径硬编码
文章里大量直接写 /Imgs/... 或其他静态路径字符串。维护体验很差:
- 资源迁移时需要手动逐篇替换
- 图像引用无静态检查,导致部署后才发现的问题
3. 未使用WebP
旧文章里同时存在几种形式:
- 原生
<img> - 原生 Markdown 图片
ts
const props = withDefaults(defineProps<{ src: string //图像文件路径 alt?: string align?: Align //图像对齐方式 wrap?: boolean //文本环绕方式 caption?: string //脚注 captionLink?: string //链接 maxHeight?: number | string //最大高度(数字单位默认px,也可以使用字符串如32rem)}>(), { alt: '', align: 'center', wrap: false, caption: '', captionLink: '', maxHeight: '32rem',})text
.├── GroupPhoto(Raw)-Convert│ ├── 乐队合照1.webp│ ├── 乐队合照2.webp│ ├── 乐队合照3.webp│ ├── 千禧年的孩子们.webp│ ├── 妮露和爱丽丝1.webp│ ├── 游戏开发部合照.webp│ ├── 游戏开发部合照2.webp│ ├── 爱丽丝和柯伊.webp│ ├── 联邦理事会长和老师.webp│ └── 阿洛娜与普拉娜.webp├── GroupPhoto(Raw)│ ├── 乐队合照1.PNG│ ├── 乐队合照2.PNG│ ├── 乐队合照3.PNG│ ├── 千禧年的孩子们.jpg│ ├── 妮露和爱丽丝1.PNG│ ├── 游戏开发部合照.jpg│ ├── 游戏开发部合照2.jpg│ ├── 爱丽丝和柯伊.PNG│ ├── 联邦理事会长和老师.jpg│ └── 阿洛娜与普拉娜.PNGts
export const path = { // /Image/Miscellaneous/essay-openai-image-2 "海报": "/Image/Miscellaneous/essay-openai-image-2/海报.webp", "漫画": "/Image/Miscellaneous/essay-openai-image-2/漫画.webp", "漫画_翻嵌": "/Image/Miscellaneous/essay-openai-image-2/漫画_翻嵌.webp", "阿洛娜与普拉娜": "/Image/Miscellaneous/essay-openai-image-2/阿洛娜与普拉娜.webp", "阿洛娜与普拉娜_原图": "/Image/Miscellaneous/essay-openai-image-2/阿洛娜与普拉娜_原图.webp", // /Image/Miscellaneous/operating-sys cover: "/Image/Miscellaneous/operating-sys/cover.webp", // /Image/Miscellaneous/operating-sys/synchronize "单一实例死锁": "/Image/Miscellaneous/operating-sys/synchronize/单一实例死锁.webp", "多实例未死锁": "/Image/Miscellaneous/operating-sys/synchronize/多实例未死锁.webp", "多实例死锁": "/Image/Miscellaneous/operating-sys/synchronize/多实例死锁.webp", // /Image/Miscellaneous/personal-profile-hobbies/Books "发现的乐趣": "/Image/Miscellaneous/personal-profile-hobbies/Books/发现的乐趣.webp", "娱乐至死": "/Image/Miscellaneous/personal-profile-hobbies/Books/娱乐至死.webp", ts
import { path as miscellaneousImagePath } from '@public/Image/Miscellaneous/path'ts
const mangaResultImage = { src: miscellaneousImagePath['漫画_翻嵌'], alt: '漫画翻嵌结果', align: 'right', wrap: true, maxHeight: '28rem', caption: '处理后:中文文本可读性明显提升,画面氛围基本保留。',} as consttsx
<Image {...mangaResultImage} />
这种写法使得
- 路径不再硬编码
- TypeScript 区域有正常高亮
- 通过Key引用图像,IDE支持静态扫描
3. build期间静态检查
截止上文的步骤结束,正确方式使用的图像组件已经具有了 IDE 高亮,意味着空引用会在编辑器中被提示。但如果只把文章当作 Markdown 渲染,pnpm build 仍然不会天然检查这些 TypeScript 片段。
text
/tmp/post-image-check-xxxx/posts_blog_change_log_01_to_cloudflare_md.ts:63:31 - error TS2339: Property 'errorTest' does not exist on type '{ readonly 海报: "/Image/Miscellaneous/essay-openai-image-2/海报.webp"; readonly 漫画: "/Image/Miscellaneous/essay-openai-image-2/漫画.webp"; readonly 漫画_翻嵌: "/Image/Miscellaneous/essay-openai-image-2/漫画_翻嵌.webp"; ... 37 more ...; readonly dns: "/Image/Miscellaneous/project-blog-to-cloudflare/dns.webp"; }'.63 src: miscellaneousImagePath.errorTest, ~~~~~~~~~Found 1 error in posts/blog-change-log-01-to-cloudflare.md:63