跳到内容
乱八七糟
返回

用 AstroPaper 搭建个人博客教程

1933 字 6 分钟

博客框架发展简史

静态站点生成器(SSG)的发展历程非常清晰:

整体趋势从「简单快速生成」逐步演进为「高性能 + 灵活组件化」。

Astro 是什么

Astro 是一个以内容为中心的 Web 框架。页面默认生成纯静态 HTML,JavaScript 只在真正需要交互的地方加载(官方叫”Islands 架构”)。

Astro 用 .astro 文件写组件(语法类似 Vue 单文件组件),也可以在组件里混用 React/Vue/Svelte。写文章用原生 Markdown 就行,也支持 MDX。

→ 官方文档:docs.astro.build

AstroPaper 简介

AstroPaper 是一个基于 Astro 的博客主题,设计简洁,SEO 友好,响应式,支持深色模式。原作者 satnaing 在持续维护。


快速开始

前置条件:

# fork 后克隆到本地
git clone https://github.com/<你的用户>/astro-paper.git
cd astro-paper

# 安装依赖并启动
pnpm install
pnpm dev

pnpm dev 默认跑在 http://localhost:4321,修改文件会自动热更新。

博客的主要配置在 src/config.ts,打开改掉标题、描述、作者信息就行。src/content.config.ts 定义了文章的 frontmatter schema,要加自定义字段在那里改。


我进行的一些改造

修改源码可查看 Astro-paper Fork

分类系统

原版只有标签,没有分类。标签太扁了,按主题分组文章更好找。

做法:用文件夹做分类。在 posts/ 下建子目录,数字前缀控制排序:

posts/
├── 01-tech/           技术
├── 02-life/           生活
├── 03-sports/         运动
├── 04-entertainment/  影音游
└── 05-thoughts/       随想

文章的 slug 会自动去掉数字前缀(01-tech/xxx.md/posts/tech/xxx)。

分类的显示名称在 src/constants.tsCATEGORIES 数组里配置,支持中/英/日三语:

export const CATEGORIES: Category[] = [
  { slug: "tech", label: { zh: "技术", en: "Tech", ja: "技術" } },
  { slug: "life", label: { zh: "生活", en: "Life", ja: "生活" } },
  // ...
];

对应生成了 /categories 列表页和 /categories/[category] 详情页。

浮动目录(TOC)

原版目录在文章开头,长文章阅读体验差。重写了一个 TableOfContents.astro 组件:

阅读进度条

文章详情页有两个进度指示:

i18n 多语言

原版 UI 文本是硬编码英文,散落在各组件里。做了客户端动态方案:

选客户端方案而不是 Astro 官方的 i18n 路由方案,是因为博客只有中文内容,没必要生成三套页面。UI 文本切换就够了。

文章列表懒加载

文章多了之后一次性渲染全部 DOM 不太合适。用 IntersectionObserver 做懒加载:

代码块增强

引入了几个 Shiki transformer:

代码主题用的是 min-light / night-owl

外链自动新标签页

写了一个 remark 插件 remarkExternalLinks,自动给所有 http(s) 链接加 target="_blank" rel="noopener noreferrer"。在 astro.config.tsremarkPlugins 里注册就行。

中英文阅读统计

原版只支持英文的阅读时间估算。自己写了一个 readingTime.ts

图片展示

接入 PhotoSuite 实现:

astro.config.ts 配置 scope: "#article",只处理文章内容区域。我的图片都存在阿里云 OSS,Markdown 里直接用完整 URL 引用。

RSS 美化

浏览器直接访问 /rss.xml 默认显示 XML 源码,不好看。加了一个 public/rss-style.xsl 样式表:

字体

没有引入 Google Fonts。直接用 CSS 系统字体栈,中文走了 PingFang SC / Hiragino Sans GB / Microsoft YaHei,覆盖 macOS 和 Windows。国内访问不受影响。


构建与部署

一键部署

pnpm build

产物在 dist/ 目录,全是静态文件。

一键部署到 Cloudflare / Vercel / Netlify,Astro 官方文档都有现成指南:

如果用自己的服务器,下面的流程可以照着做。

GitHub Actions 自部署

思路:push 代码 → GitHub Actions 自动 build → 通过 SSH 把 dist/ 同步到服务器 → nginx 托管。

有两个版本相关的坑要先知道:

服务器端生成 SSH 密钥

# 删除旧密钥(防止冲突)
sudo rm -f /root/.ssh/github-actions*

# 生成 RSA 4096 PEM 格式密钥(无密码)
sudo ssh-keygen -t rsa -b 4096 -C "github-actions-root-deploy" -m PEM -f /root/.ssh/github-actions -N ""

# 把公钥加入 authorized_keys
cat /root/.ssh/github-actions.pub >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

然后用 cat /root/.ssh/github-actions 查看私钥,复制下来。

配置 GitHub Secrets

进 GitHub 仓库 → SettingsSecrets and variablesActions,添加:

名称
SSH_PRIVATE_KEY上一步的私钥,完整粘贴
REMOTE_HOST服务器 IP 或域名
REMOTE_USERSSH 用户名(如 root
REMOTE_TARGET部署目录,如 /var/www/blog

创建 workflow 文件

在项目根目录建 .github/workflows/deploy.yml

name: Deploy Astro to Server

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

on:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v5
        with:
          version: latest

      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm

      - run: pnpm install

      - run: pnpm run build

      - uses: easingthemes/ssh-deploy@v5
        with:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
          REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
          REMOTE_USER: ${{ secrets.REMOTE_USER }}
          SOURCE: dist/
          TARGET: ${{ secrets.REMOTE_TARGET }}
          ARGS: "-rltgoDzvO --delete"

几个要点:

域名解析、nginx 配置、SSL 证书这些属于服务器运维范畴,后面单独写一篇。

→ GitHub Actions 文档:docs.github.com/actions


写作软件

写 Markdown 博客,挑顺手的就行。说几个常见的:

VS Code:写代码的人基本都装了,Markdown 预览、语法高亮、拼写检查都能通过插件搞定。我自己的做法是用 VS Code 写,配合项目里的 TypeScript 类型检查,frontmatter 字段写错了能有提示。

Obsidian:如果同时用它管理笔记,直接拿来写博客也行。实时预览、双向链接、图片拖拽这些对写作很友好,写完使用 Git 插件 push 即可。

其他:Typora(所见即所得,付费)、Neovim/Vim 等就不多说了。