本博客代码修改指南

代码修改指南

Mingrui Blog 常见改代码需求指南


目录

  1. 先理解项目怎么运行
  2. 最常改的文件速查表
  3. 新增、修改、删除博客文章
  4. 修改首页
  5. 修改关于页
  6. 修改顶部导航栏和全站布局
  7. 修改博客列表页
  8. 修改文章详情页
  9. 修改归档页
  10. 修改标签页
  11. 修改全站样式
  12. 修改文章正文样式
  13. 添加图片、头像和静态资源
  14. 添加文章封面图
  15. 添加文章阅读时间
  16. 添加上一篇 / 下一篇文章
  17. 添加 RSS
  18. 添加 sitemap
  19. 增强 SEO
  20. 添加搜索功能
  21. 文章变多后添加分页
  22. 修改 favicon
  23. 修改域名和部署配置
  24. 同步 README
  25. 清理模板遗留文件
  26. 日常开发流程
  27. 发布前检查清单
  28. 常见问题排查
  29. 建议的后续开发顺序

1. 先理解项目怎么运行

Mingrui Blog 是一个 Astro 静态博客项目。

你可以这样理解它:

Markdown / MDX 文章

Astro Content Collections 读取文章

src/pages/ 里的页面生成首页、博客列表、文章详情、归档和标签页

src/layouts/BaseLayout.astro 统一包住页面

src/styles/global.css 控制全站样式

npm run build 生成 dist/ 静态网站

平时开发主要做三件事:

npm run dev

用于本地开发预览。

npm run build

用于正式构建,检查项目是否能成功生成静态网站。

npm run preview

用于本地预览生产构建结果。


2. 最常改的文件速查表

需求主要修改位置
新增文章src/content/blog/
修改文章内容src/content/blog/某篇文章.md
修改首页src/pages/index.astro
修改关于页src/pages/about.astro
修改博客列表页src/pages/blog/index.astro
修改文章详情页src/pages/blog/[slug].astro
修改归档页src/pages/archive.astro
修改标签总览页src/pages/tags/index.astro
修改标签详情页src/pages/tags/[tag].astro
修改顶部导航栏src/layouts/BaseLayout.astro
修改全站 HTML head、meta、faviconsrc/layouts/BaseLayout.astro
修改全站样式src/styles/global.css
修改文章字段规则src/content.config.ts
修改路径拼接逻辑src/utils/basePath.ts
添加图片、favicon 等静态资源public/
修改域名astro.config.mjs
修改 npm 命令或依赖package.json
修改项目说明README.md
配置 GitHub Actions 部署.github/workflows/

3. 新增、修改、删除博客文章

3.1 新增一篇文章

文章放在:

src/content/blog/

例如新建:

src/content/blog/my-first-real-post.md

基本模板:

---
title: "我的第一篇正式文章"
description: "这是一篇关于我开始写个人博客的记录。"
pubDate: 2026-05-06
updatedDate: 2026-05-06
tags: ["Blog", "Astro", "学习"]
draft: false
---

这里开始写正文。

## 为什么我要写博客

正文内容……

## 后续计划

正文内容……

3.2 frontmatter 字段说明

文章顶部的这段叫 frontmatter:

---
title: "文章标题"
description: "文章摘要"
pubDate: 2026-05-06
updatedDate: 2026-05-06
tags: ["Astro", "Blog"]
draft: false
---

每个字段的作用:

字段是否必填作用
title文章标题
description文章摘要,也会用于列表页和 meta description
pubDate发布日期,用于排序和归档
updatedDate更新日期
tags标签数组,用于标签统计和标签页
draft是否草稿,默认通常为 false

3.3 文章 URL 是怎么来的

文章文件名会影响 URL。

例如:

src/content/blog/hello-blog.md

会生成:

/blog/hello-blog/

再比如:

src/content/blog/learning-astro.md

会生成:

/blog/learning-astro/

建议文件名使用英文、小写、短横线:

good:
learning-astro.md
my-first-project.md
how-i-built-my-blog.md

not recommended:
我的文章.md
My First Blog.md
test 1.md

3.4 写草稿

如果文章还没写完,不想公开,设置:

draft: true

例如:

---
title: "还没写完的文章"
description: "这篇文章还在草稿阶段。"
pubDate: 2026-05-06
tags: ["Draft"]
draft: true
---

草稿文章不会出现在:

  • 首页最近文章
  • 博客列表页
  • 文章详情页
  • 归档页
  • 标签总览页
  • 标签详情页

发布时再改成:

draft: false

3.5 修改文章

直接打开对应 Markdown 文件修改即可。

例如:

src/content/blog/hello-blog.md

修改标题:

title: "新的文章标题"

修改正文:

## 新的小标题

新的正文内容。

3.6 删除文章

直接删除对应 .md.mdx 文件。

例如删除:

src/content/blog/hello-blog.md

删除后重新运行:

npm run dev

或:

npm run build

检查是否还有错误。

3.7 修改文章标签

例如原来是:

tags: ["Astro", "Blog"]

想加一个“前端”:

tags: ["Astro", "Blog", "前端"]

标签会自动出现在:

/tags/

对应标签详情页会自动生成:

/tags/Astro/
/tags/Blog/
/tags/前端/

3.8 修改发布日期

例如:

pubDate: 2026-05-06

发布日期会影响:

  • 首页最近文章排序
  • 博客列表排序
  • 归档页年份分组
  • 标签页文章排序

如果文章只是更新内容,不建议改 pubDate,可以改:

updatedDate: 2026-05-10

4. 修改首页

首页文件:

src/pages/index.astro

首页路径:

/

当前首页通常负责:

  • 展示博客名称
  • 展示个人简介
  • 展示进入博客、关于页的按钮
  • 展示最近文章
  • 展示“我会写什么”
  • 展示关于页入口

4.1 修改首页标题和简介

src/pages/index.astro 中找类似内容:

<h1>Mingrui Blog</h1>
<p>记录学习、项目实践、工具使用心得和生活思考。</p>

可以改成:

<h1>Mingrui 的个人博客</h1>
<p>这里记录我的学习笔记、项目实践、技术踩坑和生活思考。</p>

4.2 修改首页按钮

可能会看到类似:

<a href={withBase('/blog/')}>阅读博客</a>
<a href={withBase('/about/')}>关于我</a>

你可以修改按钮文字:

<a href={withBase('/blog/')}>开始阅读</a>
<a href={withBase('/about/')}>了解我</a>

也可以增加一个 GitHub 按钮:

<a href="https://github.com/你的用户名" target="_blank" rel="noreferrer">
  GitHub
</a>

4.3 修改首页最近文章数量

找类似代码:

const recentPosts = posts.slice(0, 3);

如果想展示最近 5 篇:

const recentPosts = posts.slice(0, 5);

4.4 修改首页“我会写什么”

找类似结构:

<section>
  <h2>我会写什么</h2>
  <ul>
    <li>学习</li>
    <li>项目实践</li>
    <li>工具使用</li>
    <li>生活思考</li>
  </ul>
</section>

可以改成:

<section>
  <h2>我会写什么</h2>
  <ul>
    <li>前端学习笔记</li>
    <li>个人项目复盘</li>
    <li>AI 工具使用心得</li>
    <li>生活和成长记录</li>
  </ul>
</section>

4.5 首页没有文章时显示什么

博客刚开始可能文章很少,首页通常会有空状态。

例如:

{recentPosts.length === 0 && (
  <p>暂时还没有文章。</p>
)}

你可以改成:

{recentPosts.length === 0 && (
  <p>文章正在准备中,欢迎稍后再来看看。</p>
)}

5. 修改关于页

关于页文件:

src/pages/about.astro

访问路径:

/about/

5.1 修改个人介绍

找类似内容:

<h1>关于我</h1>
<p>我是 Mingrui。</p>

改成你自己的介绍:

<h1>关于我</h1>
<p>
  你好,我是 Mingrui。我正在学习前端开发、个人知识管理和 AI 工具使用。
  这个博客用来记录我的学习过程、项目实践和一些生活思考。
</p>

5.2 修改“我现在关注什么”

例如:

<h2>我现在关注什么</h2>
<ul>
  <li>Astro 和静态站点搭建</li>
  <li>前端基础</li>
  <li>AI 工具辅助学习</li>
</ul>

你可以根据自己的真实情况改。

5.3 修改联系方式

如果现在是占位链接:

<a href="#">Email</a>
<a href="#">GitHub</a>
<a href="#">LinkedIn</a>

发布前建议不要保留 #

你可以改成真实链接:

<a href="mailto:your-email@example.com">Email</a>
<a href="https://github.com/your-name" target="_blank" rel="noreferrer">GitHub</a>
<a href="https://www.linkedin.com/in/your-name/" target="_blank" rel="noreferrer">LinkedIn</a>

如果暂时不想公开联系方式,可以先删除:

<section>
  <h2>联系方式</h2>
  <p>暂时不公开联系方式。</p>
</section>

5.4 加头像

先把头像放到:

public/images/avatar.png

然后在 about.astro 里添加:

<img src={withBase('/images/avatar.png')} alt="Mingrui 的头像" class="avatar" />

再到 src/styles/global.css 添加样式:

.avatar {
  width: 120px;
  height: 120px;
  border-radius: 999px;
  object-fit: cover;
}

6. 修改顶部导航栏和全站布局

核心布局文件:

src/layouts/BaseLayout.astro

它负责:

  • 引入全局样式
  • 设置 HTML head
  • 设置网页 title
  • 设置 description
  • 设置 favicon
  • 设置 Open Graph
  • 输出顶部 header
  • 输出导航栏
  • <slot /> 放具体页面内容

6.1 修改网站名

找类似内容:

<a href={withBase('/')}>Mingrui Blog</a>

改成:

<a href={withBase('/')}>Mingrui 的博客</a>

6.2 添加导航项

原来可能是:

<nav>
  <a href={withBase('/')}>首页</a>
  <a href={withBase('/blog/')}>博客</a>
  <a href={withBase('/archive/')}>归档</a>
  <a href={withBase('/about/')}>关于</a>
</nav>

如果想加“标签”:

<nav>
  <a href={withBase('/')}>首页</a>
  <a href={withBase('/blog/')}>博客</a>
  <a href={withBase('/archive/')}>归档</a>
  <a href={withBase('/tags/')}>标签</a>
  <a href={withBase('/about/')}>关于</a>
</nav>

6.3 修改网页默认标题格式

页面一般会传入:

<BaseLayout title="首页标题" description="页面描述">

BaseLayout.astro 中可能有:

<title>{title}</title>

如果你想统一加站点名:

<title>{title ? `${title} | Mingrui Blog` : 'Mingrui Blog'}</title>

注意:如果文章详情页已经传了 "文章标题 - Mingrui Blog",就不要重复加两次。你需要统一全站标题规则。

6.4 修改全站 description

BaseLayout.astro 里找:

<meta name="description" content={description} />

每个页面传入的 description 会显示在这里。

如果某些页面没有传,可以设置默认值:

const {
  title = 'Mingrui Blog',
  description = 'Mingrui 的个人博客,记录学习、项目实践、工具使用心得和生活思考。',
} = Astro.props;

6.5 修改 favicon 引用

可能会看到:

<link rel="icon" href={withBase('/favicon.svg')} />

如果你换成 PNG:

<link rel="icon" type="image/png" href={withBase('/favicon.png')} />

对应文件要放到:

public/favicon.png

7. 修改博客列表页

文件:

src/pages/blog/index.astro

访问路径:

/blog/

7.1 修改列表页标题

找:

<h1>博客</h1>

可以改成:

<h1>全部文章</h1>

7.2 修改文章卡片展示内容

文章列表一般会循环:

{posts.map((post) => (
  <article class="post-item">
    <h2>
      <a href={withBase(`/blog/${post.id}/`)}>{post.data.title}</a>
    </h2>
    <p>{post.data.description}</p>
  </article>
))}

你可以增加日期:

<time datetime={post.data.pubDate.toISOString()}>
  {post.data.pubDate.toLocaleDateString('zh-CN')}
</time>

可以增加标签:

<div class="tags">
  {post.data.tags.map((tag) => (
    <a href={withBase(`/tags/${tag}/`)} class="tag">{tag}</a>
  ))}
</div>

7.3 修改排序方式

通常会看到:

posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

这是从新到旧。

如果想从旧到新:

posts.sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf());

7.4 隐藏草稿逻辑

通常会有:

const posts = (await getCollection('blog')).filter((post) => !post.data.draft);

这表示过滤掉草稿。

不要随便删掉这段,否则 draft: true 的文章也可能被公开。


8. 修改文章详情页

文件:

src/pages/blog/[slug].astro

访问路径类似:

/blog/hello-blog/

8.1 修改文章标题区域

你可能会看到:

<h1>{post.data.title}</h1>
<p>{post.data.description}</p>

可以改成:

<header class="post-header">
  <h1>{post.data.title}</h1>
  <p class="post-description">{post.data.description}</p>
</header>

然后到 global.css 添加样式:

.post-header {
  margin-bottom: 2rem;
}

.post-description {
  color: #666;
  font-size: 1.1rem;
}

8.2 修改日期显示

可能有:

<time>{post.data.pubDate.toLocaleDateString('zh-CN')}</time>

你可以显示更完整:

<p>
  发布于:
  <time datetime={post.data.pubDate.toISOString()}>
    {post.data.pubDate.toLocaleDateString('zh-CN')}
  </time>
</p>

显示更新时间:

{post.data.updatedDate && (
  <p>
    更新于:
    <time datetime={post.data.updatedDate.toISOString()}>
      {post.data.updatedDate.toLocaleDateString('zh-CN')}
    </time>
  </p>
)}

8.3 修改正文区域

文章正文一般来自:

<Content />

或者类似:

<div class="content">
  <Content />
</div>

.content 的样式通常在:

src/styles/global.css

如果你想修改正文的段落、标题、代码块、引用样式,主要改 .content 相关 CSS。

8.4 添加返回按钮

例如:

<a href={withBase('/blog/')}>← 返回博客列表</a>

可以改成:

<nav class="post-nav">
  <a href={withBase('/blog/')}>← 查看全部文章</a>
</nav>

9. 修改归档页

文件:

src/pages/archive.astro

访问路径:

/archive/

9.1 修改归档页标题

<h1>归档</h1>

可以改成:

<h1>文章归档</h1>

9.2 当前按年份分组

归档页一般会按照 pubDate 的年份分组。

例如:

2026
  05-06 文章 A
  05-05 文章 B

9.3 如果想按月份分组

可以把分组 key 从年份改成年月。

原本可能类似:

const year = post.data.pubDate.getFullYear();

改成:

const year = post.data.pubDate.getFullYear();
const month = String(post.data.pubDate.getMonth() + 1).padStart(2, '0');
const key = `${year}-${month}`;

这样可以分成:

2026-05
2026-04
2026-03

9.4 修改归档项显示内容

原来可能只显示:

<a href={withBase(`/blog/${post.id}/`)}>{post.data.title}</a>

可以加摘要:

<p>{post.data.description}</p>

也可以加标签:

<div class="tags">
  {post.data.tags.map((tag) => (
    <a href={withBase(`/tags/${tag}/`)} class="tag">{tag}</a>
  ))}
</div>

10. 修改标签页

标签相关文件:

src/pages/tags/index.astro
src/pages/tags/[tag].astro

10.1 标签总览页

文件:

src/pages/tags/index.astro

访问路径:

/tags/

它负责统计所有标签,并显示每个标签下有几篇文章。

10.2 修改标签排序

当前通常是:

  1. 文章数量多的排前面
  2. 数量相同按中文排序

如果你想按标签名称排序:

tags.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'));

如果想按数量排序:

tags.sort((a, b) => b.count - a.count);

10.3 标签详情页

文件:

src/pages/tags/[tag].astro

访问路径:

/tags/Astro/

它展示某个标签下的全部文章。

10.4 修改标签页文章卡片

和博客列表页类似,可以在循环里修改:

{posts.map((post) => (
  <article class="post-item">
    <h2>
      <a href={withBase(`/blog/${post.id}/`)}>{post.data.title}</a>
    </h2>
    <p>{post.data.description}</p>
  </article>
))}

11. 修改全站样式

文件:

src/styles/global.css

这是美化博客最常用的文件。

11.1 修改全站字体

可能会看到:

body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

你可以改成:

body {
  font-family:
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    "PingFang SC",
    "Microsoft YaHei",
    sans-serif;
}

11.2 修改背景色

原来:

body {
  background: #fafafa;
}

改成更温和的背景:

body {
  background: #f7f7f5;
}

11.3 修改正文颜色

body {
  color: #222;
}

可以改成:

body {
  color: #1f2933;
}

11.4 修改页面最大宽度

可能有:

main {
  max-width: 760px;
}

如果想变宽:

main {
  max-width: 860px;
}

如果想文章阅读更窄:

main {
  max-width: 720px;
}

11.5 修改顶部 header 宽度

可能有:

.site-header {
  max-width: 860px;
}

可以改成:

.site-header {
  max-width: 960px;
}

11.6 修改文章卡片

可能有:

.post-item {
  background: #fff;
  border: 1px solid #e5e5e5;
  border-radius: 8px;
  padding: 1rem;
}

可以改成更柔和:

.post-item {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 12px;
  padding: 1.25rem;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04);
}

11.7 修改链接颜色

a {
  color: #2563eb;
}

可以改成:

a {
  color: #0f766e;
}

11.8 修改标签样式

可能有:

.tag {
  border-radius: 999px;
  padding: 0.2rem 0.6rem;
}

可以改成:

.tag {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  padding: 0.25rem 0.7rem;
  background: #f1f5f9;
  color: #475569;
  font-size: 0.85rem;
  text-decoration: none;
}

11.9 修改移动端样式

通常在 CSS 底部会有:

@media (max-width: 640px) {
  ...
}

你可以在里面改手机端布局。

例如:

@media (max-width: 640px) {
  main {
    padding: 1rem;
  }

  .site-header {
    flex-direction: column;
    align-items: flex-start;
  }
}

12. 修改文章正文样式

文章正文通常包在:

<div class="content">
  ...
</div>

所以重点改:

src/styles/global.css

里面的 .content 相关样式。

12.1 修改段落行高

.content p {
  line-height: 1.8;
}

如果觉得太松:

.content p {
  line-height: 1.7;
}

12.2 修改标题间距

.content h2 {
  margin-top: 2rem;
  margin-bottom: 1rem;
}

12.3 修改引用样式

.content blockquote {
  border-left: 4px solid #ddd;
  padding-left: 1rem;
  color: #666;
}

可以改成:

.content blockquote {
  border-left: 4px solid #94a3b8;
  padding: 0.75rem 1rem;
  background: #f8fafc;
  color: #475569;
}

12.4 修改代码块样式

行内代码:

.content code {
  background: #f1f5f9;
  padding: 0.15rem 0.35rem;
  border-radius: 4px;
}

代码块:

.content pre {
  overflow-x: auto;
  padding: 1rem;
  border-radius: 8px;
  background: #0f172a;
}

12.5 修改图片样式

.content img {
  max-width: 100%;
  border-radius: 8px;
  display: block;
  margin: 1.5rem auto;
}

13. 添加图片、头像和静态资源

13.1 推荐放到 public/images/

新建目录:

public/images/

放图片:

public/images/avatar.png
public/images/post-cover.jpg
public/images/project-demo.png

13.2 在页面中引用

如果不用 withBase()

<img src="/images/avatar.png" alt="头像" />

如果使用 withBase()

<img src={withBase('/images/avatar.png')} alt="头像" />

为了兼容未来 base 路径,建议使用 withBase()

13.3 在 Markdown 文章中引用图片

例如图片放在:

public/images/astro-note.png

Markdown 中写:

![Astro 笔记截图](/images/astro-note.png)

如果未来部署到子路径,这种写法可能需要额外处理。当前自定义域名根路径下通常没问题。

13.4 图片命名建议

推荐:

avatar.png
astro-blog-cover.jpg
project-demo-01.png

不推荐:

图片1.png
我的 截图.png
Screen Shot 2026-05-06 at 10.00.png

建议使用英文、小写、短横线。


14. 添加文章封面图

这是一个常见进阶功能。

需要改三个地方:

  1. 文章 frontmatter
  2. src/content.config.ts
  3. 页面展示位置

14.1 修改文章 frontmatter

例如:

---
title: "我开始学习 Astro"
description: "记录我学习 Astro 的过程。"
pubDate: 2026-05-06
tags: ["Astro", "学习"]
cover: "/images/covers/astro-learning.jpg"
draft: false
---

14.2 修改 content schema

打开:

src/content.config.ts

找到 blog schema,加入:

cover: z.string().optional(),

大概像这样:

const blog = defineCollection({
  loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    cover: z.string().optional(),
  }),
});

14.3 在文章列表页显示封面

修改:

src/pages/blog/index.astro

在文章卡片里加:

{post.data.cover && (
  <img class="post-cover" src={withBase(post.data.cover)} alt={post.data.title} />
)}

14.4 在文章详情页显示封面

修改:

src/pages/blog/[slug].astro

在标题下方或正文上方加入:

{post.data.cover && (
  <img class="post-cover-large" src={withBase(post.data.cover)} alt={post.data.title} />
)}

14.5 添加样式

修改:

src/styles/global.css

加入:

.post-cover {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 10px;
  margin-bottom: 1rem;
}

.post-cover-large {
  width: 100%;
  max-height: 420px;
  object-fit: cover;
  border-radius: 12px;
  margin: 2rem 0;
}

15. 添加文章阅读时间

阅读时间可以简单按字数估算。

15.1 新建工具函数

新建:

src/utils/readingTime.ts

写入:

export function getReadingTime(text: string) {
  const wordsPerMinute = 300;
  const textLength = text.trim().length;
  const minutes = Math.max(1, Math.ceil(textLength / wordsPerMinute));

  return `${minutes} 分钟阅读`;
}

中文文章可以按字符数粗略估算。

15.2 在文章详情页使用

打开:

src/pages/blog/[slug].astro

需要拿到文章正文原始内容时,具体实现取决于当前 Astro 内容对象是否暴露 body。

如果可以访问 post.body,可以写:

---
import { getReadingTime } from '../../utils/readingTime';

const readingTime = getReadingTime(post.body ?? '');
---

<p>{readingTime}</p>

如果当前对象没有 body,可以先只在列表中不做阅读时间,或者以后使用 rehype / remark 插件实现。

15.3 更简单的方式

先手动写字段:

---
title: "文章标题"
description: "文章摘要"
pubDate: 2026-05-06
tags: ["Blog"]
readingTime: "5 分钟阅读"
draft: false
---

然后在 src/content.config.ts 加:

readingTime: z.string().optional(),

页面中显示:

{post.data.readingTime && <span>{post.data.readingTime}</span>}

这种方式最简单,但需要每篇文章手动写。


16. 添加上一篇 / 下一篇文章

修改:

src/pages/blog/[slug].astro

16.1 思路

在文章详情页中:

  1. 获取所有非草稿文章
  2. 按发布时间排序
  3. 找到当前文章的位置
  4. 得到上一篇和下一篇
  5. 在页面底部显示链接

16.2 示例代码思路

在 frontmatter 脚本区域中:

const allPosts = (await getCollection('blog'))
  .filter((item) => !item.data.draft)
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

const currentIndex = allPosts.findIndex((item) => item.id === post.id);

const previousPost = allPosts[currentIndex + 1];
const nextPost = allPosts[currentIndex - 1];

页面底部:

<nav class="post-pagination">
  {previousPost && (
    <a href={withBase(`/blog/${previousPost.id}/`)}>
      ← {previousPost.data.title}
    </a>
  )}

  {nextPost && (
    <a href={withBase(`/blog/${nextPost.id}/`)}>
      {nextPost.data.title} →
    </a>
  )}
</nav>

样式:

.post-pagination {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid #e5e7eb;
}

17. 添加 RSS

RSS 可以让别人通过阅读器订阅你的博客。

17.1 安装依赖

npm install @astrojs/rss

17.2 新建 RSS 页面

新建:

src/pages/rss.xml.js

写入示例:

import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';

export async function GET(context) {
  const posts = (await getCollection('blog'))
    .filter((post) => !post.data.draft)
    .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

  return rss({
    title: 'Mingrui Blog',
    description: 'Mingrui 的个人博客',
    site: context.site,
    items: posts.map((post) => ({
      title: post.data.title,
      description: post.data.description,
      pubDate: post.data.pubDate,
      link: `/blog/${post.id}/`,
    })),
  });
}

17.3 在导航或 head 里添加 RSS 链接

BaseLayout.astro<head> 中加入:

<link rel="alternate" type="application/rss+xml" title="Mingrui Blog RSS" href={withBase('/rss.xml')} />

也可以在 footer 或 about 页面添加:

<a href={withBase('/rss.xml')}>RSS</a>

18. 添加 sitemap

sitemap 有助于搜索引擎发现页面。

18.1 安装依赖

npm install @astrojs/sitemap

18.2 修改 astro.config.mjs

import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://potatotom.top',
  integrations: [sitemap()],
});

18.3 检查构建

npm run build

构建后应该会生成 sitemap 相关文件。


19. 增强 SEO

当前项目已经有基础 description 和 Open Graph。后续可以继续增强。

主要修改:

src/layouts/BaseLayout.astro
src/pages/blog/[slug].astro
astro.config.mjs

19.1 添加 canonical URL

BaseLayout.astro 中增加 props:

interface Props {
  title: string;
  description: string;
  canonical?: string;
}

然后:

{canonical && <link rel="canonical" href={canonical} />}

页面调用时传入:

<BaseLayout
  title="页面标题"
  description="页面描述"
  canonical={new URL(Astro.url.pathname, Astro.site).toString()}
>

19.2 文章页使用 article 类型

BaseLayout.astro 中可以增加:

<meta property="og:type" content={ogType ?? 'website'} />

props:

ogType?: string;

文章详情页传:

<BaseLayout
  title={`${post.data.title} - Mingrui Blog`}
  description={post.data.description}
  ogType="article"
>

19.3 添加文章发布时间 meta

文章详情页可以在 <head> 里提供更多信息。

一种做法是让 BaseLayout 支持额外 slot:

<head>
  ...
  <slot name="head" />
</head>

文章页中:

<Fragment slot="head">
  <meta property="article:published_time" content={post.data.pubDate.toISOString()} />
  {post.data.updatedDate && (
    <meta property="article:modified_time" content={post.data.updatedDate.toISOString()} />
  )}
</Fragment>

19.4 添加 Open Graph 图片

如果实现了 cover 字段,可以在 BaseLayout 中支持:

image?: string;

然后:

{image && <meta property="og:image" content={image} />}

文章页传:

image={post.data.cover ? new URL(post.data.cover, Astro.site).toString() : undefined}

20. 添加搜索功能

搜索功能可以先做简单版本。

20.1 简单方案:前端搜索

适合文章数量不多的个人博客。

思路:

  1. 构建一个 JSON 文件,包含所有文章标题、描述、标签和链接
  2. 前端页面读取 JSON
  3. 用输入框过滤

20.2 新建搜索数据接口

新建:

src/pages/search.json.js

示例:

import { getCollection } from 'astro:content';

export async function GET() {
  const posts = (await getCollection('blog'))
    .filter((post) => !post.data.draft)
    .map((post) => ({
      title: post.data.title,
      description: post.data.description,
      tags: post.data.tags,
      url: `/blog/${post.id}/`,
    }));

  return new Response(JSON.stringify(posts), {
    headers: {
      'Content-Type': 'application/json',
    },
  });
}

20.3 新建搜索页面

新建:

src/pages/search.astro

可以先做一个简单页面:

---
import BaseLayout from '../layouts/BaseLayout.astro';
import { withBase } from '../utils/basePath';
---

<BaseLayout title="搜索 - Mingrui Blog" description="搜索 Mingrui Blog 的文章">
  <h1>搜索</h1>

  <input id="search-input" type="search" placeholder="输入关键词搜索文章" />
  <ul id="search-results"></ul>

  <script>
    const input = document.querySelector('#search-input');
    const results = document.querySelector('#search-results');

    async function loadPosts() {
      const response = await fetch('/search.json');
      return response.json();
    }

    const posts = await loadPosts();

    input?.addEventListener('input', () => {
      const keyword = input.value.toLowerCase().trim();

      const matched = posts.filter((post) => {
        return (
          post.title.toLowerCase().includes(keyword) ||
          post.description.toLowerCase().includes(keyword) ||
          post.tags.join(' ').toLowerCase().includes(keyword)
        );
      });

      results.innerHTML = matched
        .map((post) => `<li><a href="${post.url}">${post.title}</a></li>`)
        .join('');
    });
  </script>
</BaseLayout>

注意:如果未来有 base 子路径,fetch('/search.json') 也需要调整。

20.4 导航栏增加搜索入口

修改:

src/layouts/BaseLayout.astro

加入:

<a href={withBase('/search/')}>搜索</a>

21. 文章变多后添加分页

现在文章少,不需要分页。

如果文章超过 30 篇,可以考虑分页。

21.1 博客列表分页

Astro 支持 paginate

可以把:

src/pages/blog/index.astro

改成:

src/pages/blog/[page].astro

示例思路:

---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { withBase } from '../../utils/basePath';

export async function getStaticPaths({ paginate }) {
  const posts = (await getCollection('blog'))
    .filter((post) => !post.data.draft)
    .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

  return paginate(posts, { pageSize: 10 });
}

const { page } = Astro.props;
---

<BaseLayout title="博客 - Mingrui Blog" description="Mingrui Blog 的全部文章">
  <h1>博客</h1>

  {page.data.map((post) => (
    <article class="post-item">
      <h2>
        <a href={withBase(`/blog/${post.id}/`)}>{post.data.title}</a>
      </h2>
      <p>{post.data.description}</p>
    </article>
  ))}

  <nav>
    {page.url.prev && <a href={page.url.prev}>上一页</a>}
    {page.url.next && <a href={page.url.next}>下一页</a>}
  </nav>
</BaseLayout>

21.2 注意事项

分页会影响 URL 结构。

例如:

/blog/1/
/blog/2/

你需要考虑是否保留 /blog/ 作为第一页。

初期不建议急着做分页,等文章数量真的变多再做。


22. 修改 favicon

当前 favicon 在:

public/favicon.svg
public/favicon.ico

22.1 替换 favicon.svg

准备一个新的 SVG 文件,命名为:

favicon.svg

覆盖:

public/favicon.svg

22.2 替换 favicon.ico

如果你也有 .ico 文件,覆盖:

public/favicon.ico

22.3 检查 BaseLayout

打开:

src/layouts/BaseLayout.astro

确认里面引用了:

<link rel="icon" href={withBase('/favicon.svg')} />

如果你改成 PNG:

<link rel="icon" type="image/png" href={withBase('/favicon.png')} />

对应放到:

public/favicon.png

23. 修改域名和部署配置

配置文件:

astro.config.mjs

23.1 自定义域名

当前类似:

export default defineConfig({
  site: 'https://potatotom.top',
});

如果换域名:

export default defineConfig({
  site: 'https://new-domain.com',
});

23.2 如果部署到 GitHub Pages 子路径

例如:

https://username.github.io/Mingrui_Blog/

可能需要:

export default defineConfig({
  site: 'https://username.github.io',
  base: '/Mingrui_Blog/',
});

这时 withBase() 的价值就会体现出来。

23.3 GitHub Actions 部署

当前 .github/workflows/ 为空。

如果要自动部署,需要添加:

.github/workflows/deploy.yml

具体内容取决于你部署到哪里:

  • GitHub Pages
  • Cloudflare Pages
  • Vercel
  • Netlify
  • 自己的服务器

不同平台配置不同,不建议在还没确定部署平台前乱写 workflow。


24. 同步 README

当前 README 应该和真实项目状态保持一致。

建议 README 至少包含:

# Mingrui Blog

Mingrui 的个人博客,使用 Astro 构建。

## 技术栈

- Astro
- Markdown / MDX
- TypeScript
- CSS

## 本地开发

```bash
npm install
npm run dev

构建

npm run build

预览构建结果

npm run preview

目录说明

  • src/content/blog/:博客文章
  • src/pages/:页面路由
  • src/layouts/:布局
  • src/styles/global.css:全局样式
  • public/:静态资源

写文章

src/content/blog/ 中新增 Markdown 文件:

---
title: "文章标题"
description: "文章摘要"
pubDate: 2026-05-06
tags: ["Blog"]
draft: false
---

正文内容。

注意:如果当前还没有 GitHub Actions,就不要写“已经配置自动部署”。

---

# 25. 清理模板遗留文件

当前可能有模板遗留资源:

```text
src/assets/astro.svg
src/assets/background.svg

如果项目里没有使用,可以删除。

25.1 删除前先搜索

在 VS Code 中全局搜索:

astro.svg
background.svg

如果没有任何引用,可以删除。

25.2 删除后检查构建

npm run build

如果构建成功,说明没有问题。


26. 日常开发流程

26.1 开始开发

npm install
npm run dev

如果已经安装过依赖,以后只需要:

npm run dev

26.2 修改代码

常见修改:

写文章 → src/content/blog/
改首页 → src/pages/index.astro
改关于页 → src/pages/about.astro
改样式 → src/styles/global.css
改导航 → src/layouts/BaseLayout.astro

26.3 本地检查

浏览器打开本地开发地址,检查:

  • 首页是否正常
  • 博客列表是否正常
  • 文章详情页是否正常
  • 标签页是否正常
  • 归档页是否正常
  • 移动端是否正常

26.4 构建检查

发布前一定运行:

npm run build

如果失败,先修复错误再发布。

26.5 预览生产构建

npm run preview

这比 npm run dev 更接近真实发布效果。


27. 发布前检查清单

发布前建议检查:

内容检查

  • 没有草稿文章误设为 draft: false
  • 没有公司 confidential 信息
  • 没有客户数据
  • 没有内部代码
  • 没有会议记录
  • 没有截图泄露隐私
  • 没有 token、API key、密码
  • 文章标题没有错别字
  • 文章日期正确
  • 标签合理

页面检查

  • 首页正常
  • 博客列表正常
  • 文章详情页正常
  • 归档页正常
  • 标签页正常
  • 关于页正常
  • 导航链接都能点开
  • 404 情况可以接受

样式检查

  • 桌面端排版正常
  • 手机端排版正常
  • 图片没有超出页面
  • 代码块可以横向滚动
  • 链接颜色清晰
  • 深浅颜色对比足够

构建检查

  • npm run build 成功
  • npm run preview 预览正常
  • dist/ 不手动修改
  • node_modules/ 不提交
  • .astro/ 不提交

28. 常见问题排查

28.1 新文章没有出现在博客列表

检查:

  1. 文件是否放在 src/content/blog/
  2. 文件后缀是否是 .md.mdx
  3. frontmatter 是否正确
  4. draft 是否为 true
  5. pubDate 格式是否正确

正确示例:

---
title: "文章标题"
description: "文章摘要"
pubDate: 2026-05-06
tags: ["Blog"]
draft: false
---

28.2 页面报错:frontmatter 字段不合法

可能是 src/content.config.ts 的 schema 不允许这个字段。

例如你写了:

cover: "/images/cover.jpg"

但 schema 没有定义 cover

解决方法是在 src/content.config.ts 里加:

cover: z.string().optional(),

28.3 日期报错

日期推荐写:

pubDate: 2026-05-06

不推荐乱写:

pubDate: May 6
pubDate: today

28.4 图片不显示

检查:

  1. 图片是否在 public/images/
  2. 路径是否写对
  3. 文件名大小写是否一致
  4. 是否使用了中文空格或特殊字符
  5. 部署路径是否需要 withBase()

28.5 链接跳转 404

检查:

  1. 是否少了 /
  2. 是否需要 withBase()
  3. 文件名和 URL 是否一致
  4. 标签名是否包含特殊字符

推荐内部链接写:

<a href={withBase('/blog/')}>博客</a>

28.6 build 失败

先看终端报错。

常见原因:

  • Markdown frontmatter 格式错
  • TypeScript 类型错误
  • 引用了不存在的文件
  • import 路径写错
  • 新增字段没有更新 schema
  • 删除文件后还有引用

28.7 样式没变化

检查:

  1. 是否改的是 src/styles/global.css
  2. BaseLayout.astro 是否引入了 global.css
  3. 浏览器是否缓存
  4. class 名是否写对
  5. CSS 选择器优先级是否不够

29. 建议的后续开发顺序

不建议一开始就做复杂功能。

推荐顺序:

第一阶段:内容可用

  1. 跑通项目
  2. 写 2-3 篇真实文章
  3. 修改关于页
  4. 修改首页文案
  5. 替换联系方式

第二阶段:视觉统一

  1. 修改全站颜色
  2. 修改字体和排版
  3. 优化文章卡片
  4. 优化正文阅读体验
  5. 替换 favicon
  6. 添加头像

第三阶段:内容增强

  1. 添加文章封面图
  2. 添加上一篇 / 下一篇
  3. 添加阅读时间
  4. 添加 RSS
  5. 添加 sitemap
  6. 增强 SEO

第四阶段:文章多了再做

  1. 分页
  2. 搜索
  3. 标签描述
  4. 系列文章
  5. 评论系统

最后建议

这个项目目前已经具备个人博客的核心功能。后续不要急着重构,建议优先做这几件事:

  1. 多写真实文章
  2. 把关于页改成真实个人介绍
  3. 把 README 和实际项目状态同步
  4. 稳定后再加 RSS、SEO、搜索和分页

维护个人博客最重要的不是功能复杂,而是结构清楚、能长期写下去。