Astro主题使用技巧

记录使用 Astro 搭建博客的过程中的一些使用技巧,包括图片优化、代码高亮等等。

博客源代码:chensoul.github.io

图片优化

参考 Photosuite:一个为博客而生的图片处理方案,使用 Photosuite 优化博客图片。

使用 markdown 语法设置图片

![example-picture](example-picture.webp)

注意图片路径前面没有 /,图片地址是相对路径。Photosuite 会将图片地址替换为 {imageBase}/example-picture.webp,imageBase 在 astro.config.ts 中由 photosuite 配置。

本文中的实际示例图使用站内绝对路径,避免构建文档时去请求外部 EXIF 服务。

example-picture

如果使用绝对路径,比如 ![example-picture](/images/example-picture.webp), Photosuite 不会进行替换。 渲染后的 html 如下:

<div class="photosuite-item"><a href="/images/example-picture.webp" data-fancybox="markdown">
<img src="/images/example-picture.webp" alt="example-picture" loading="lazy" decoding="async"></a>
<div class="photosuite-caption">example-picture</div>
</div>

也可以将 2 个或者 3 个图片排列到一起:

![example-picture](example-picture.webp)
![example-picture](example-picture.webp)
![example-picture](example-picture.webp)
![example-picture](example-picture.webp)
![example-picture](example-picture.webp)

astro.config.ts 中配置

astro.config.ts
photosuite({
scope: '#article',
imageBase: "https://cos.chensoul.cc/images",
exif: {
enabled: true,
fields: [
'Model', // Camera Model
'LensModel', // Lens Model
'FocalLength', // Focal Length
'FNumber', // Aperture
'ExposureTime', // Shutter Speed
'ISO', // ISO
'DateTimeOriginal' // Date Original
],
separator: ' · ' // Separator
},
}),

https://cos.chensoul.cc/images 对应的是 obs 存储里面的 images 目录,这里我使用的是 Cloudflare R2 对象存储。cos.chensoul.cc 是对象存储桶(桶名称为 cos)对应的自定义域名,该存储桶下目前有三个目录:

images 和 thumbs 在博客源码仓库的 public 目录有一个备份。使用 rclone 工具可以同步 public 目录下的这两个文件到 R2。

使用 Rclone 上传图片

安装和配置 rclone:

Terminal window
brew install rclone
rclone config file

~/.config/rclone/rclone.conf 文件如下:

~/.config/rclone/rclone.conf
[r2]
type = s3
provider = Cloudflare
access_key_id = xxxx
secret_access_key = xxxxx
endpoint = https://2e1980a0ef43f57b920ea28cdf9d38d1.r2.cloudflarestorage.com
acl = private
no_check_bucket = true

操作桶和对象:

Terminal window
rclone tree r2:cos
# 同步到桶里的子目录,例如 cos 桶下的 images。不删远程多余文件(默认就是这样), -P 显示进度
rclone sync public/images r2:cos/images -P
rclone sync public/thumbs r2:cos/thumbs -P
# 看会同步哪些文件,不真正执行(试跑)
rclone sync public/images r2:cos/images -P --dry-run
# 多线程、显示传输进度
rclone sync public/images r2:cos/images -P --transfers 4

图片压缩和转换

在博客源代码仓库的 scripts/convert-to-webp.mjs 里合并了两种用法(均用 sharp):

图片压缩

一键压缩 public/images 下所有 jpg/png/webp:

Terminal window
pnpm run compress-images

只压缩指定子目录(相对项目根):

Terminal window
node scripts/convert-to-webp.mjs public/images/某个子目录 --compress

说明:

图片转换

把 public/images 下所有 jpg/png 转成 webp(转完后删除原图):

Terminal window
pnpm run convert-to-webp

只转换指定目录:

Terminal window
node scripts/convert-to-webp.mjs public/images/某子目录

保留原图,只多出一份 .webp:

Terminal window
pnpm run convert-to-webp -- --keep
# 或指定目录
node scripts/convert-to-webp.mjs public/images --keep

说明:

文章缩略图

文章缩略图需要在博客文章的 md 文件元数据中定义 cover 字段,以指定缩略图的图片地址(该地址以 / 开头),thumbspublic 下面。

cover: /thumbs/astro-logo.svg

缩略图的渲染由 Card.astro 实现,其中最重要的一段代码是获取缩略图的地址:

Card.astro
const buildCoverSrc = (): string | undefined => {
const resolveLocalSrc = (src: string): string => {
const normalizedSrc = src.startsWith("/") ? src : `/${src}`;
if (import.meta.env.DEV) {
return normalizedSrc;
}
const imagesBase = (SITE.imageConfig.imagesUrl ?? "")
.trim()
.replace(/\/$/, "");
return imagesBase ? `${imagesBase}${normalizedSrc}` : normalizedSrc;
};
const rawImage = typeof image === "string" ? image.trim() : "";
if (rawImage) {
if (rawImage.startsWith("http://") || rawImage.startsWith("https://")) {
return rawImage;
}
return resolveLocalSrc(rawImage);
}

构建封面图 URL 逻辑:

config.ts 中定义了图片的 CDN 地址 https://cos.chensoul.cc

config.ts
imageConfig: {
imagesUrl: "https://cos.chensoul.cc",
}

提示:使用 convertio 网站可以将图片转换为 SVG,然后使用 SVG Viewer 可以调整 SVG 大小,最后作为文章的缩略图。

代码高亮

Expressive Code 对静态架构非常友好,只要你的博客支持 Rehype 插件,就可以安装使用。

功能语法示例说明
标题title="app.ts"显示编辑器标签标题
语法高亮(ANSI)ansi渲染 ANSI 终端转义
行标注(高亮){3,5-9}高亮第 3 行与 5–9 行
行标注(新增)ins={4-6}以“新增”样式高亮
行标注(删除)del={7,12}以“删除”样式高亮
行标注 + 标签ins={"Init":3-6}为 3–6 行添加引用标签
内联标注(文本)ins="inserted" del="deleted"高亮匹配到的片段
内联标注(正则)ins=/foo$.+$/用正则匹配片段
折叠区块collapse={1-5,12-14}折叠多段代码
行号开关showLineNumbers需安装行号插件
起始行号startLineNumber=5从第 5 行开始计数
自动换行wrap自动换行
悬挂缩进wrap hangingIndent=2换行时额外缩进 2 列
不保留缩进wrap preserveIndent=false换行后不缩进

示例:

示例
```go title="main.go" {17-20,22} del={14} ins={15,4-7} collapse={2-7} "rand.Seed(time.Now().UnixNano())"
func greet(id int, wg *sync.WaitGroup) {
defer wg.Done()
delay := time.Duration(rand.Intn(3000)) * time.Millisecond
time.Sleep(delay)
fmt.Printf("#%d Ping %v\n", id, delay)
}
func main() {
rand.Seed(time.Now().UnixNano())
var wg sync.WaitGroup
numGoroutines := 10
numGoroutines := 5
for i := 1; i <= numGoroutines; i++ {
wg.Add(1)
go greet(i, &wg)
}
wg.Wait()
}
```

效果:

main.go
func greet(id int, wg *sync.WaitGroup) {
6 collapsed lines
defer wg.Done()
delay := time.Duration(rand.Intn(3000)) * time.Millisecond
time.Sleep(delay)
fmt.Printf("#%d Ping %v\n", id, delay)
}
func main() {
rand.Seed(time.Now().UnixNano())
var wg sync.WaitGroup
numGoroutines := 10
numGoroutines := 5
for i := 1; i <= numGoroutines; i++ {
wg.Add(1)
go greet(i, &wg)
}
wg.Wait()
}

订阅文章

订阅更新,不错过后续文章

直接通过 RSS 和 Telegram 订阅本站更新。

订阅 RSS关注 Telegram

分享文章

如果这篇有帮助,可以顺手转发

直接分享给同事、朋友,或者发到你的社交平台。

分享到 X 分享到 Telegram 邮件分享
Pig AI 新版体验
博客从 Hugo 迁移到 Astro