使用Astro重构博客和文档站

之前一直使用 Hugo 做博客框架,文档站就使用比较简单的 VitePress 作为框架,但是这两个框架在我这里体验都不是非常地好,相应速度上面都多多少少差点意思,最后在 AI 的推荐之下尝试了一个全新的框架 —— Astro。

博客

博客在我这里的定位更多是按照时间线记录自己探索与实践文章的地方,所以重点是时间以及个人介绍,所以我看上了这个主题 —— Fuwari

主题安装

打开主题的 GitHub 项目

根据项目的 REDME 文档进行主题的安装,我选择使用 pnpm 包管理器进行安装,所以需要先安装 pnpm 这个包管理器

1
npm install -g pnpm

复制 README 文档当中的主题安装命令进行安装

1
pnpm create fuwari@latest

根据提示输入项目文件夹即可,要求输入自己博客的名称,subtitle等默认即可,自己输入也不会在配置文件当中体现。

主题预览与配置文件修改

主题预览,进入项目文件夹,输入命令进行预览

1
pnpm dev

配置文件位置如下图所示

文章位置则是在上方 post 文件夹内,文章需要写专门的 Frontmatter,格式如下

1
2
3
4
5
6
7
8
9
10
---
title: My First Blog Post
published: 2023-09-09
description: This is the first post of my new Astro blog.
image: ./cover.jpg
tags: [Foo, Bar]
category: Front-end
draft: false
lang: jp # Set only if the post's language differs from the site's language in `config.ts`
---

这个博客有一个缺点,就是没有友链的模板,不能直接使用,我自己添加了一个自己的模板,在这里也将模板写出来

title
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
import { getEntry, render } from "astro:content";
import Markdown from "@components/misc/Markdown.astro";
import MainGridLayout from "../layouts/MainGridLayout.astro";

const friendsPost = await getEntry("spec", "friends");

if (!friendsPost) {
throw new Error("Friends page content not found");
}

const { Content } = await render(friendsPost);
---
<!-- 页面标题在 src/content/spec/friends.md 里,导航入口在 src/config.ts -->
<MainGridLayout title="友链" description="友链">
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32">
<div class="card-base z-10 px-9 py-6 relative w-full">
<Markdown class="mt-2">
<Content />
</Markdown>
</div>
</div>
</MainGridLayout>
title
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# 友链



欢迎与我交换友链!请先添加本站,再通过邮件或 Telegram 联系我,并附上你的站点信息:



- 站点名称

- 站点地址

- 头像地址

- 一句话简介



确认后我会尽快把好友添加到下面的列表里~



<div id="friends-links" class="not-prose" role="list" aria-label="友链列表"></div>



<style>

/* 友链网格布局 */

#friends-links {

  margin: 20px 0;

  display: grid;

  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));

  gap: 16px;

}



/* 友链卡片图标样式 */

.friend-card .friend-icon {

  width: 52px;

  height: 52px;

  border-radius: 12px;

  flex-shrink: 0;

  position: relative;

  background: var(--btn-card-bg);

  border: 2px solid var(--btn-card-border);

  overflow: hidden;

  transition: transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;

  transform-origin: center center;

}



.friend-card:hover .friend-icon {

  transform: scale(1.1);

  border-color: oklch(0.7 0.15 var(--hue));

  box-shadow: 0 4px 14px oklch(0.6 0.15 var(--hue) / 0.3);

}



.friend-card .friend-icon .icon-placeholder {

  position: absolute;

  inset: 0;

  display: flex;

  align-items: center;

  justify-content: center;

  font-size: 1.25rem;

  font-weight: 700;

  color: white;

  background: linear-gradient(135deg, oklch(0.65 0.15 var(--hue)), oklch(0.55 0.18 var(--hue)));

}



.friend-card .friend-icon .icon-img {

  position: absolute;

  top: 50%;

  left: 50%;

  transform: translate(-50%, -50%);

  max-width: 80%;

  max-height: 80%;

}

</style>



<script>

(function () {

  // 友链数据:添加或修改友链只需编辑下面的数组

  // 字段说明:name(站点名称) / desc(一句话简介) / url(站点地址) / logo(图标地址)

  const friendLinks = [

  { name: "InkNoteWell", desc: "我的文档知识库,更新中……", url: "https://docs.ivoinkwell.xyz", logo: "https://docs.ivoinkwell.xyz/apple-touch-icon-167x167.webp" },

  { name: "Inkwell Drive", desc: "我的网盘,博客以及文档站文章中出现的附件均可以在网盘中下载", url: "https://drive.ivoinkwell.xyz", logo: "https://ivoinkwell.xyz/favicon/favicon.ico" },

  { name: "Quick Reference", desc: "一个好用的编程知识点速查网站", url: "https://wangchujiang.com/reference/", logo: "https://wangchujiang.com/reference/icons/touch-icon-iphone-retina.webp" },

  { name: "云泽の小屋", desc: "最好的朋友,他的博客是世界上最详细网络安全实践笔记!", url: "https://zeyun.org", logo: "https://zeyun.org/media/website/Happy_Mac.webp" },

  { name: "猪老师在线", desc: "最有能力的老师,网络安全没人比他讲的更好!", url: "https://www.pigteacher.com", logo: "https://www.pigteacher.com/wp-content/uploads/2025/01/cropped-1-180x180.jpg" },

  { name: "云泽の小屋-网站推荐", desc: "优秀网站的收藏夹", url: "https://zeyun.org/good_web.html", logo: "https://zeyun.org/media/website/Happy_Mac.webp" },
  ];



  const container = document.getElementById("friends-links");

  if (!container) return;



  friendLinks.forEach((item) => {

    const initial = [...item.name.trim()][0] || "?";

    const card = document.createElement("a");

    card.className =

      "friend-card no-styling card-base group flex items-start gap-4 p-4 hover:bg-[var(--btn-card-bg-hover)] active:bg-[var(--btn-card-bg-active)] active:scale-95";

    card.href = item.url;

    card.target = "_blank";

    card.rel = "noopener noreferrer";

    card.setAttribute("role", "listitem");

    card.setAttribute("aria-label", `访问 ${item.name} - ${item.desc}`);



    card.innerHTML = `

      <span class="friend-icon">

        <span class="icon-placeholder">${initial}</span>

        <img class="icon-img" src="${item.logo}" alt="${item.name}" loading="lazy" onerror="this.style.display='none'" />

      </span>

      <span class="flex-1 min-w-0">

        <span class="flex items-center gap-1.5 font-bold text-lg text-90">

          <span class="truncate">${item.name}</span>

          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14" class="text-30 opacity-0 group-hover:opacity-100 transition flex-shrink-0"><path stroke-linecap="round" stroke-linejoin="round" d="M7 17 17 7M9 7h8v8"/></svg>

        </span>

        <span class="block text-sm text-75 mt-1 line-clamp-2">${item.desc}</span>

      </span>

    `;



    container.appendChild(card);

  });

})();

</script>

按照路径复制进去之后更改即可获得这样的一个页面

文档站

文档站我主要用于记录一个系列的文章,所以保持 VitePress 的简洁样式我就觉得很好,所以选择了 Starlight + Starlight Nova 主题。


其实可以猜猜为什么截图是浅色模式,倒不是因为主题不支持,而是深色模式有的地方显示确实对比度太高而有点瞎眼

这个就是深色模式的代码展示,对比度看着确实不是非常舒服,但是我坚持使用这个主题只有一个原因,就是因为简洁的外观,所以使用了这个主题。

创建文档站

点击下方文字跳转到官方说明页面

点击打开说明页面

根据说明使用包管理器进行安装,我依然选择使用 pnpm 包管理器进行安装

1
pnpm create astro --template starlight

项目预览

和博客相同,进入项目后输入一下命令即可,构建速度会比博客快很多

1
pnpm dev

主题安装

首先选择自己喜欢的主题,可以 在官方主题选择页面 进行选择

我选择的是 Starlight Nova 这个主题,进入官方安装页面进行安装

1
pnpm add starlight-theme-nova

随后对配置文件进行编辑

title
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// @ts-check

import { defineConfig } from 'astro/config';

import starlight from '@astrojs/starlight';

import starlightThemeNova from 'starlight-theme-nova'



// https://astro.build/config

export default defineConfig({

    integrations: [

        starlight({

            plugins: [starlightThemeNova(/* options */), ],

            title: 'InkNoteWell',

            description: 'Notes with Ink',

安装完成之后即可显示出上方展示页面

这个地方的图片我不知道怎么称呼,我就称呼为 banner 了,也就是下面这个地方

这个地方有一个坑,就是只能显示正方形图片,不相信你可以用新的标签页打开看

这个就是一个正方形,所以如果使用异性图片要会自己 PS ,使用网页版也可以解决,也就是 Photopea ,将图片补全成为一个正方形导出即可,但是 补全的地方背景要进行删除 。

Frontmatter

这个相比博客可以简单一些,例如这样

1
2
3
---
title: '我的第一篇文章'
---

这样的话直接将一级标题作为文章标题去写文章最后将其加上 Frontmatter 也会更省事一点。

加载速度优化

用 Astro 重构我的本质意图还是为了加载与渲染的性能,所以做一些图片的 CDN 配置是很有必要的,所以我做了图床的 CDN 策略,没有图床也可以根据我的配置去改变一些博客域名的配置

我选择在边缘缓存一年,像是博客的静态图片可以选择这样的策略,也不会影响文字的正常变更

至此,加载速度应该已经是免费版本 Cloudflare 托管的极限了。


使用Astro重构博客和文档站
https://blog.ivoinkwell.xyz/specks/refactoring-blogs-and-documentation-sites-with-astro/
作者
Ivo
发布于
2026年8月22日
许可协议