图片与静态资源
图片与静态资源
Vergil 沿用 Astro 官方的做法:图片集中存放,构建时自动压缩、转 WebP、写入宽高。在此基础上加了一个 @img/ 别名,不管文章放在哪一层目录,引用路径都一样。
两种引用方式
| 方式 | 写法 | 图片放在 | 构建时 | 用在哪 |
|---|---|---|---|---|
@img/ 别名 | @img/blog/my-post/a.png | src/assets/img/ | 压缩、转 WebP、写入宽高 | 内容的图:正文图片、封面(banner、专栏 cover)、seo.image、知识库、相册、图文动态、想法,以及内容指令里的图 |
/assets/ 绝对路径 | /assets/site/avatar.jpg | public/assets/ | 原样输出 | 站点的图:头像、分享卡片图、开屏背景;以及 private 加密内容里的图 |
原则是:内容相关的图放 src/assets/,public/ 只放站点本身的文件。 外链 https://... 在两类位置都能用。
目录全景
src/assets/img/ ← @img/ 指向这里├── blog/<文章名>/ 博客文章的图├── projects/<项目名>/ 项目的图├── pages/<页面名>/ 独立页面的图,比如 pages/about/├── docs/<知识库>/ 知识库的图,含 _meta.md 的封面├── albums/<相册名>/ 相册的封面和照片├── series/<专栏名>/ 专栏封面├── moments/<文件名>/ 图文动态的图└── thoughts/<文件名>/ 想法的图src/assets/videos/ 相册主题的背景视频,构建时带内容哈希输出public/├── favicon.svg、manifest.json 必须放在根路径的文件└── assets/ ← /assets/ 指向这里 ├── site/ 站点图:头像、分享卡片图 ├── defaults/ 主题的默认图:开屏背景、动态页横幅,没配置时使用 └── <按需建>/ private 加密内容用到的图src/assets/img/ 下的目录名和 src/content/ 下的目录名一一对应。
@img/:内容里的图
图片放在 src/assets/img/<载体>/<内容名>/,引用写 @img/<载体>/<内容名>/<图片名>。
- 载体是
src/content/下的目录名:blog、projects、pages、docs、albums、series、moments、thoughts。 - 内容名是文件名去掉
.md。放在专栏子目录里的文章也只取文件名,和文章 URL 的规则一致。 - 文档按知识库分:不同知识库里常有同名文件,所以写
@img/docs/<知识库>/<图片名>。
src/assets/img/blog/├── starrocks-compaction/│ └── trigger-flow.png└── 短链服务/ └── architecture.pngsrc/content/blog/├── starrocks-compaction.md └── 系统设计/ └── 短链服务.md 主题自带的示例就是这样放的,可以照着改:
- 写第一篇文章:放在专栏子目录
搭建你的第一个站点/里的文章,含正文图片、banner和seo.image - 山里的一个周末:普通文章,横幅、摄影框和画廊里的图都用
@img/ - 专栏「搭建你的第一个站点」的封面:
src/assets/img/series/搭建你的第一个站点/cover.svg,在 专栏列表 里能看到效果 - 本页的这张图:知识库文档里的图片,放在
src/assets/img/docs/vergil-guide/


@img/ 定义在 tsconfig.json 的 paths 里,由 Astro 原生解析。写法和文章所在的目录无关,文章挪进专栏子目录,路径也不用改。
正文里:

frontmatter 里声明为图片类型的字段也能用。@ 开头的值在 YAML 里必须加引号,否则构建报错:
---
seo:
image:
src: '@img/blog/starrocks-compaction/cover.jpg'
---
支持 @img/ 的字段:博客和文档的 banner,专栏的 cover,博客、页面、项目的 seo.image.src,知识库 _meta.md 的 cover,相册的 cover 和 images[].src,图文动态 JSON 里的 images 和 linkCard.image,想法正文里的图片。
banner 和专栏 cover 也兼容 / 开头的 public/ 路径和外链,老内容不用改也能用。
构建后图片输出到 /_astro/,文件名带内容哈希,比如 trigger-flow.TDdz2LGr.webp,并自动带上 width、height 和 loading="lazy"。
内容指令里的图
所有内容指令里的图片都和正文图片一样,支持 @img/、相对路径和外链,本地图片会被优化:
| 指令 | 放图片的地方 |
|---|---|
image、photo | src |
gallery、story | 里面的  |
banner | bg、avatar |
video、audio | poster、cover |
yoicard | bg-image、logo |
button、quot、title | icon、prefix、suffix,写图片地址时生效,写 Iconify 图标名不受影响 |
sites、posters | 图片在 src/data/config/links.ts 里配置,icon、cover 写 '@img/...' 或外链 |
::image{src="@img/blog/my-post/a.png" alt="架构图"}
:::video{src="https://example.com/v.mp4" poster="@img/blog/my-post/poster.jpg"}
:::
例外只有一个:private 加密内容。它的正文在构建时被加密成字符串,里面的图片没法交给 Astro,只能写外链或 /assets/ 路径。
/assets/:站点的图
public/assets/ 放站点本身用到的图,以及 private 加密内容里的图。这里的文件原样输出,不会被压缩。
站点图片都在配置里写路径:
| 图片 | 文件 | 在哪配置 |
|---|---|---|
| 头像(侧栏、动态页、简历共用) | public/assets/site/avatar.jpg | src/data/site-config.ts 的 avatar |
| 分享卡片图 | public/assets/site/vergil-preview.jpg | src/data/config/identity.ts 的 image |
| 开屏背景 | public/assets/defaults/splash-*.webp | src/data/config/splash.ts 的 backgroundImage |
换成自己的图,同名覆盖,或者把配置里的路径改成你自己的文件。
写作时不用管规则
写的时候图片放哪、路径怎么写都行,比如 Typora 粘贴截图生成的相对路径。pnpm build 会先运行 pnpm images:organize,把图片挪到规则目录、引用改成 @img/,再开始构建。
想在编辑器里修改已经整理过的文章时,先展开:
pnpm images:expand它把 @img/xxx 改成指向 src/assets/img/xxx 的相对路径,比如 ../../assets/img/blog/my-post/a.png,Typora、VS Code 就能预览了。图片文件不会移动。展开后的写法 pnpm dev 和 pnpm build 都能正常解析,下次 pnpm build 时会自动改回 @img/。
展开范围是博客、项目、页面、知识库、相册、专栏、想法。图文动态是 JSON,编辑器预览不了,保持 @img/ 写法。
用 pnpm images:organize 整理
已经写好的内容里,图片如果还散在别处(相对路径、public/ 下),运行一次就能按上面的规则整理好:
pnpm images:organize它会:
- 找出博客、项目、页面、知识库、相册、专栏、想法、图文动态里还没用
@img/的本地图片引用,包括正文图片,banner、cover、seo.image.src、相册images[].src、图文动态的images,以及内容指令里的图片 src/assets/img/之外的图片移动到src/assets/img/<载体>/<内容名>/;已经在src/assets/img/里的图片不挪,只统一写法- 把引用改成
@img/...,frontmatter 里自动加引号 - 最后自动跑一次
pnpm images:check
几种特殊情况:
| 情况 | 处理 |
|---|---|
图片已经在 src/assets/img/ 里(比如放在自己建的 series/icons/ 公共目录) | 不挪文件,只把相对路径改写成 @img/。多条内容可以直接共用这一份 |
src/assets/img/ 之外的同一张图被多条内容引用 | 移给第一条,其余各复制一份;同一知识库内只放一份 |
| 图片还被代码、配置或其他字符串字段引用 | 只复制,不移动,原文件留在原处 |
| 目标位置已有同名但内容不同的文件 | 自动改名,比如 flow-1.png |
| 引用的文件不存在 | 跳过,在结果里列出来 |
不会做的事:不删除任何图片,没被引用的图原地不动;不处理 private 加密内容里的图片;代码块和行内代码里的写法示例不动。
命令直接修改文件,建议在 git 工作区干净的时候运行,整理完用 git diff 看一遍改动。已经整理好的内容再运行一次,不会有任何变化。
整理结果里如果有失效引用,只会列出来,不会让命令失败,所以不会挡住 pnpm build。要在 CI 里把失效引用当成错误,用 pnpm images:check。
用 pnpm images:check 体检
仓库自带一个图片检查脚本,只读不写,不会删除或移动任何文件:
pnpm images:check它会报告五类问题:
| 类别 | 含义 | 是否让命令失败 |
|---|---|---|
| 引用失效 | 内容里写了图片路径,但对应文件不存在 | 是 |
| 可以迁移 | 正文引用了 public/ 下的图,挪到 src/assets/img/ 能被优化 | 否 |
| 内容重复 | 几个文件的内容完全相同 | 否 |
| 体积过大 | 单张超过 500KB | 否 |
| 暂未引用 | 没有在任何源码或内容里找到这个文件名 | 否 |
只有「引用失效」会让命令以非零状态退出,适合放进 CI。其余几类只是提示,尤其是「暂未引用」的图,可能是留着以后用的,脚本不会建议删除。
体积阈值默认 500KB,要调整就改 scripts/check-images.mjs 顶部的 MAX_KB。
脚本判断一张图有没有被用到,是看它的文件名有没有出现在源码或内容里。用字符串拼接出来的路径(比如 `post-${n}.jpg`)检测不到,会被误报成暂未引用。