输入关键词,开始搜索

↑↓ 选择↵ 打开esc 关闭
沉浸

图片与静态资源

图片与静态资源

Vergil 沿用 Astro 官方的做法:图片集中存放,构建时自动压缩、转 WebP、写入宽高。在此基础上加了一个 @img/ 别名,不管文章放在哪一层目录,引用路径都一样。

两种引用方式

方式写法图片放在构建时用在哪
@img/ 别名@img/blog/my-post/a.pngsrc/assets/img/压缩、转 WebP、写入宽高内容的图:正文图片、封面(banner、专栏 cover)、seo.image、知识库、相册、图文动态、想法,以及内容指令里的图
/assets/ 绝对路径/assets/site/avatar.jpgpublic/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      ![](@img/blog/starrocks-compaction/trigger-flow.png)└── 系统设计/    └── 短链服务.md              ![](@img/blog/短链服务/architecture.png)

主题自带的示例就是这样放的,可以照着改:

  • 写第一篇文章:放在专栏子目录 搭建你的第一个站点/ 里的文章,含正文图片、banner 和 seo.image
  • 山里的一个周末:普通文章,横幅、摄影框和画廊里的图都用 @img/
  • 专栏「搭建你的第一个站点」的封面:src/assets/img/series/搭建你的第一个站点/cover.svg,在 专栏列表 里能看到效果
  • 本页的这张图:知识库文档里的图片,放在 src/assets/img/docs/vergil-guide/

山峰

![山峰](@img/docs/vergil-guide/mountains.jpg)

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

正文里:

![Compaction 触发流程](@img/blog/starrocks-compaction/trigger-flow.png)

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、photosrc
gallery、story里面的 ![](...)
bannerbg、avatar
video、audioposter、cover
yoicardbg-image、logo
button、quot、titleicon、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.jpgsrc/data/site-config.ts 的 avatar
分享卡片图public/assets/site/vergil-preview.jpgsrc/data/config/identity.ts 的 image
开屏背景public/assets/defaults/splash-*.webpsrc/data/config/splash.ts 的 backgroundImage

换成自己的图,同名覆盖,或者把配置里的路径改成你自己的文件。

写作时不用管规则

写的时候图片放哪、路径怎么写都行,比如 Typora 粘贴截图生成的相对路径。pnpm build 会先运行 pnpm images:organize,把图片挪到规则目录、引用改成 @img/,再开始构建。

想在编辑器里修改已经整理过的文章时,先展开:

Bash
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/ 下),运行一次就能按上面的规则整理好:

Bash
pnpm images:organize

它会:

  1. 找出博客、项目、页面、知识库、相册、专栏、想法、图文动态里还没用 @img/ 的本地图片引用,包括正文图片,banner、cover、seo.image.src、相册 images[].src、图文动态的 images,以及内容指令里的图片
  2. src/assets/img/ 之外的图片移动到 src/assets/img/<载体>/<内容名>/;已经在 src/assets/img/ 里的图片不挪,只统一写法
  3. 把引用改成 @img/...,frontmatter 里自动加引号
  4. 最后自动跑一次 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 体检

仓库自带一个图片检查脚本,只读不写,不会删除或移动任何文件:

Bash
pnpm images:check

它会报告五类问题:

类别含义是否让命令失败
引用失效内容里写了图片路径,但对应文件不存在是
可以迁移正文引用了 public/ 下的图,挪到 src/assets/img/ 能被优化否
内容重复几个文件的内容完全相同否
体积过大单张超过 500KB否
暂未引用没有在任何源码或内容里找到这个文件名否

只有「引用失效」会让命令以非零状态退出,适合放进 CI。其余几类只是提示,尤其是「暂未引用」的图,可能是留着以后用的,脚本不会建议删除。

体积阈值默认 500KB,要调整就改 scripts/check-images.mjs 顶部的 MAX_KB。

「暂未引用」是按文件名匹配的

脚本判断一张图有没有被用到,是看它的文件名有没有出现在源码或内容里。用字符串拼接出来的路径(比如 `post-${n}.jpg`)检测不到,会被误报成暂未引用。

未知标题
00:00
00:00
播放列表