Astro + Fuwari 博客搭建:从主题到个人化改造
搭个人博客最容易卡住的地方不是「能不能跑起来」,而是跑起来之后不像自己的东西——首页还是模板味,文章卡片没有记忆点,详情页信息太杂,评论和音乐播放器又散落在各种教程里。
这篇按我当前博客的实际结构写:先用 Astro + Fuwari 搭出基础,再逐步接入音乐播放器、Giscus 评论、首页 Hero 改造、文章列表卡片、文章详情页布局和部署检查。目标不是复刻原主题,而是把它改成适合长期写技术笔记的个人站。
一、整体方案
当前博客的核心是:
| 模块 | 选择 | 作用 |
|---|---|---|
| 静态框架 | Astro | 生成静态页面,适合部署到 EdgeOne Pages、Cloudflare Pages、Vercel |
| 主题基础 | Fuwari | 提供文章系统、归档、RSS、Markdown 渲染、目录、明暗主题 |
| 样式体系 | Tailwind CSS + Stylus | 快速写布局,统一主题变量 |
| 页面切换 | Swup | 页面过渡和局部更新 |
| 评论 | Giscus | 使用 GitHub Discussions 做评论区 |
| 音乐 | APlayer + Meting | 底部悬浮播放器 |
| 搜索 | Pagefind | 构建后生成本地搜索索引 |
为什么不从零写主题?
| 方案 | 优点 | 问题 |
|---|---|---|
| 从零写 Astro 博客 | 结构完全可控 | 文章集合、RSS、分页、目录、Markdown 插件都要自己补 |
| 直接用 Fuwari | 功能完整 | 首页和文章页模板味比较重 |
| 在 Fuwari 上改 | 保留成熟功能,同时改视觉 | 需要读懂布局和组件之间的关系 |
这里走第三种:保留 Fuwari 的内容系统,重写关键视觉层。
二、初始化 Astro + Fuwari
如果是新项目,可以直接用 Fuwari 模板初始化;如果已经有仓库,就拉下来安装依赖。
pnpm install本地启动:
pnpm run dev构建:
pnpm run build预览构建结果:
pnpm run preview当前项目的核心命令在 package.json:
{ "scripts": { "dev": "astro dev", "check": "astro check", "build": "astro build && pagefind --site dist", "preview": "astro preview", "new-post": "node scripts/new-post.js", "format": "biome format --write ./src", "lint": "biome check --write ./src" }}构建命令后面接了 pagefind --site dist,意思是 Astro 先生成静态站点,再给 dist 目录建立搜索索引。
三、先改全站配置
站点基础配置集中在:
src/config.ts这里改站点标题、语言、主题色、导航、头像、社交链接:
export const siteConfig = { title: "Daimon's Blog", subtitle: "在折腾中生活,在探索中成长", lang: "zh_CN", themeColor: { hue: 105, fixed: false, },};hue 是主题色相,范围是 0-360。例如:
| 色相 | 大致颜色 |
|---|---|
0 | 红色 |
60 | 黄色 |
105 | 偏绿色 |
200 | 青色 |
240 | 蓝色 |
270 | 紫色 |
330 | 粉色 |
导航也在同一个文件里:
export const navBarConfig = { links: [ LinkPreset.Home, LinkPreset.Archive, LinkPreset.About, { name: "常用工具", url: "https://webtools.example.com/", external: true, icon: "fa6-solid:screwdriver-wrench", }, ],};只改导航文字、链接、图标时,优先改 src/config.ts。只有要改 Dock 外观时,才去动 src/components/Navbar.astro。
四、整理目录结构
日常真正高频修改的是这些位置:
| 路径 | 作用 |
|---|---|
src/content/posts/ | 博客文章 |
src/content/spec/about.md | 关于页 |
src/config.ts | 站点配置、导航、头像、社交链接 |
| PicList + 七牛云 | 头像、轮播图、文章封面 |
src/assets/images/ | 迁移前原图备份 |
src/components/misc/HeroSection.astro | 首页 Hero |
src/components/PostCard.astro | 首页文章卡片 |
src/pages/posts/[...slug].astro | 文章详情页 |
src/components/misc/Giscus.astro | 评论系统 |
src/components/FloatingMusicPlayer.astro | 音乐播放器 |
src/layouts/MainGridLayout.astro | 全站布局骨架 |
src/styles/markdown.css | Markdown 正文样式 |
src/styles/variables.styl | 全站颜色变量 |
生成目录不要手动改:
dist/.astro/node_modules/这些目录会由构建工具生成。手动改了也不会成为真正的源码。
五、写文章和封面
文章放在:
src/content/posts/可以用脚本新建:
pnpm run new-post my-note也可以直接写 Markdown。Frontmatter 示例:
---title: Astro + Fuwari 博客搭建:从主题到个人化改造published: 2025-04-28description: 记录从 Astro + Fuwari 初始化个人博客,到接入音乐播放器、Giscus 评论、首页 Hero、文章卡片、详情页布局、部署检查的完整改造流程。image: http://imagebed.daimona.cn/astro-fuwari-blog-guide.pngtags: ["Astro", "Fuwari", "博客", "Giscus", "APlayer", "前端"]category: 博客---封面图通过 PicList 上传到七牛云:
https://imagebed.daimona.cn上传成功后,把完整 URL 写入 Frontmatter:
image: http://imagebed.daimona.cn/<slug>.png首页文章卡片使用 16:9 比例,推荐封面尺寸:
| 尺寸 | 说明 |
|---|---|
1280x720 | 最低建议 |
1600x900 | 推荐 |
1920x1080 | 更清晰,适合后续复用 |
图片会使用 object-fit: cover 裁切,所以重要内容不要贴边。
六、接入音乐播放器
音乐播放器组件在:
src/components/FloatingMusicPlayer.astro当前使用 APlayer + Meting:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"><script is:inline src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script><script is:inline src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>
<meting-js server="netease" type="playlist" id="17997617137" fixed="true" mini="true" order="random" loop="all" volume="0.7" lrc-type="1"></meting-js>常改参数:
| 参数 | 作用 | 示例 |
|---|---|---|
server | 音乐平台 | netease |
type | 类型 | playlist、song、album |
id | 歌单或歌曲 ID | 17997617137 |
fixed | 固定在页面底部 | true |
mini | 迷你模式 | true |
order | 播放顺序 | random |
loop | 循环方式 | all |
volume | 默认音量 | 0.7 |
组件在全站布局里挂载:
<FloatingMusicPlayer />位置在:
src/layouts/Layout.astro这样所有页面都会显示播放器,不需要每个页面单独引入。
七、接入 Giscus 评论
评论组件在:
src/components/misc/Giscus.astro配置集中成一个对象:
const giscusConfig = { repo: "daimon3332/Giscus-daimin-blog", repoId: "R_kgDOSqHPHA", category: "Announcements", categoryId: "DIC_kwDOSqHPHM4C-ABX", mapping: "pathname", strict: "0", reactionsEnabled: "1", emitMetadata: "0", inputPosition: "bottom", theme: "preferred_color_scheme", lang: "zh-CN",};Giscus 的核心是 GitHub Discussions:
| 字段 | 说明 |
|---|---|
repo | 用来存评论的 GitHub 仓库 |
repoId | 仓库 ID |
category | Discussions 分类名 |
categoryId | 分类 ID |
mapping | 页面和讨论的映射方式 |
theme | 评论区主题 |
lang | 评论区语言 |
文章详情页里直接引入:
import Giscus from "@components/misc/Giscus.astro";
<Giscus />当前项目把评论放在正文后面,路径是:
src/pages/posts/[...slug].astro这样每篇文章都有独立评论区,映射方式用 pathname,URL 不变时评论就不会丢。
八、改首页 Hero
首页 Hero 组件在:
src/components/misc/HeroSection.astro它做了几件事:
| 功能 | 实现位置 |
|---|---|
| 背景轮播 | heroSlides 保存七牛云 URL |
| 花瓣飘落 | .petal-layer 和 @keyframes petal-fall |
| 圆形头像 | profileConfig.avatar |
| 打字机文案 | .typewriter |
| GitHub 链接 | profileConfig.links |
| 个人标签 | personalTags |
轮播图配置:
const heroSlides = [ "http://imagebed.daimona.cn/terminal-bg.webp", "http://imagebed.daimona.cn/wallhaven-9o2km8.webp",];轮播切换参数:
const carouselInterval = 3000;const fadeDuration = 1200;const zoomDuration = 4200;打字机文字:
const emoText = "有些人像黄昏,来时温柔,去时荒凉";个人标签:
const personalTags = [ { label: "VPS 驯兽师", href: "/archive/" }, { label: "逆向爱好者", href: "/archive/" }, { label: "服务器炼金术士", href: "/archive/" }, { label: "NixOS 信徒", href: "/archive/" }, { label: "Root 玩家", href: "/archive/" }, { label: "终端效率洁癖", href: "/archive/" },];这里的设计原则是:首页只负责建立气质,不负责塞满信息。文章列表往下滚就能看到,Hero 区只保留头像、文案、个人入口和几个关键词。
九、改文章卡片
首页文章卡片在:
src/components/PostCard.astro当前卡片结构是:
<article class:list={["note-card", className]} style={style}> <a href={url} class="note-link" aria-label={title}> <div class="note-cover-wrap"> <ImageWrapper class="note-cover" /> </div>
<div class="note-body"> <h2>{title}</h2> <p>{description}</p> <div class="note-meta-row"> <time>{published}</time> <span class="word-count">字数</span> <span class="note-tags">标签</span> </div> </div> </a></article>封面比例由 CSS 控制:
.note-cover-wrap { width: 100%; aspect-ratio: 16 / 9; overflow: hidden; background: #07080d;}
.note-cover img { width: 100%; height: 100%; object-fit: cover;}卡片只保留这些信息:
| 信息 | 保留原因 |
|---|---|
| 封面 | 建立视觉记忆 |
| 标题 | 文章入口 |
| 摘要 | 帮读者判断内容 |
| 日期 | 判断新旧 |
| 字数 | 判断阅读成本 |
| 标签 | 判断主题 |
分类没有放在卡片里,因为标签已经足够表达主题。分类适合归档统计,不适合每张卡片都展示。
十、改文章详情页
文章详情页在:
src/pages/posts/[...slug].astro当前顶部只保留:
| 元素 | 说明 |
|---|---|
| 标题 | 页面核心 |
| 头像 | 作者识别 |
| 作者名 | 个人博客标识 |
| 日期 | 发布时间 |
| 标签 | 当前文章主题 |
结构大致是:
<header class="post-header"> <h1>{entry.data.title}</h1>
<div class="post-author-line"> <img src={profileConfig.avatar || ""} alt={profileConfig.name} /> <span>{profileConfig.name}</span> <time>{formatDateToYYYYMMDD(entry.data.published)}</time> <span class="post-tags"> {entry.data.tags.map((tag) => <span class="post-tag">#{tag}</span>)} </span> </div></header>
<Markdown> <Content /></Markdown>
<Giscus />删掉了这些默认信息:
| 被删内容 | 原因 |
|---|---|
| PV 次数 | 当前没有稳定统计系统,先不放 |
| 文章地址复制块 | 对个人博客正文干扰大 |
| 作者信息大卡片 | 顶部头像已经够了 |
| 许可协议块 | 页脚和站点说明已经覆盖 |
正文宽度由 MainGridLayout.astro 控制:
isPostPage ? "grid grid-cols-1 xl:grid-cols-[1fr_minmax(0,64rem)_16rem] 2xl:grid-cols-[1fr_minmax(0,70rem)_17rem]" : "grid grid-cols-1"也就是:
左侧空白 / 中间正文 / 右侧目录中间正文最大宽度在大屏下是 64rem 到 70rem,比原主题更宽,适合长教程。
十一、保留右侧目录
目录逻辑在:
src/layouts/MainGridLayout.astrosrc/components/widget/TOC.astro只在文章页显示:
{siteConfig.toc.enable && isPostPage && ( <div id="toc-wrapper"> <TOC headings={headings}></TOC> </div>)}开关在 src/config.ts:
toc: { enable: true, depth: 3,}长教程必须保留目录。技术文章动不动几千字,没有目录就很难回到某个小节。
十二、改全站布局
布局核心在:
src/layouts/MainGridLayout.astro当前首页和文章页走不同布局:
| 页面 | 布局 |
|---|---|
| 首页 | 单列内容,顶部 Hero |
| 文章页 | 中间正文 + 右侧目录 |
| 关于页 / 归档页 | 普通内容布局 |
判断文章页:
const isPostPage = Astro.url.pathname.startsWith(url("/posts/"));首页是否显示 Hero:
const isHomePage = pathsEqual(Astro.url.pathname, url("/"));const showHomeBanner = siteConfig.banner.enable && isHomePage;页面最大宽度在:
src/constants/constants.tsexport const PAGE_WIDTH = 96;首页每页文章数量也在这里:
export const PAGE_SIZE = 8;这些属于「常改但不要乱改」的参数。宽度改太大,正文阅读会散;每页文章太多,首页加载和浏览节奏都会变差。
十三、接入部署检查
每次改完先跑:
pnpm run checkpnpm run buildEdgeOne Pages 这类静态托管平台通常填:
构建命令:pnpm run build输出目录:dist包管理器:pnpm推送流程:
git statusgit add .git commit -m "更新博客"git push如果平台已经绑定仓库,git push 后会自动构建。
十四、常见故障速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 首页封面不显示 | 图床 URL 写错 | 检查 URL 能否直接访问 |
| 本地正常,线上图片失败 | 图床防盗链配置错误 | 检查博客域名是否在 Referer 白名单 |
| Giscus 不显示 | 仓库或分类 ID 错 | 重新到 giscus.app 生成配置 |
| 评论每篇文章串在一起 | mapping 配错 | 个人博客建议用 pathname |
| 音乐播放器没出现 | CDN 被拦或脚本未加载 | 检查浏览器 Network 和 Console |
| TOC 点击不跳转 | 标题 slug 或 Swup 容器问题 | 检查 rehype-slug 和 #toc 容器 |
| 构建后搜索无结果 | Pagefind 没跑 | 确认 build 命令包含 pagefind --site dist |
构建时看到这些不一定是错误:
Pagefind doesn't support stemming for the language zh-cn.意思是中文没有英文那种词根匹配,搜索仍然可用。
Browserslist: browsers data (caniuse-lite) is old.意思是浏览器兼容性数据库旧了,不会直接导致构建失败。
十五、几条实用经验
- 先改配置,再改组件:标题、导航、头像、链接都在
src/config.ts,不要一开始就钻组件。 - 首页只负责气质:轮播图、头像、emo 文案、个人标签够了,不要把所有功能都塞进首屏。
- 文章卡片不要展示太多字段:标题、摘要、日期、字数、标签已经足够。
- 详情页顶部要克制:技术教程读者是来看内容的,不是看一堆元信息。
- 评论单独做组件:以后从 Giscus 换到别的系统,只改
Giscus.astro和详情页引用。 - 音乐播放器全站挂载:放在
Layout.astro,不要每个页面重复引。 - 所有常改点加标记:代码里用
★ 常改标出来,几个月后回来还能快速找到入口。 - 写新教程前先看规范:
src/content/posts/tutorial-style-guide.md是文章风格基准,标题、表格、代码块、结尾都按它来。