QinBlog
Binary Lyra Blog
使用 Astro 构建的个人博客。技术栈为 Astro + Svelte 5 + Tailwind CSS v4,静态输出(SSG)。
技术栈
- 框架:Astro 6(
output: "static",trailingSlash: "ignore") - 交互岛屿:Svelte 5(runes 语法)
- 样式:Tailwind CSS v4(
@tailwindcss/vite,CSS-first)+ CSS 自定义属性(配色/暗黑模式变量) - 内容:Astro Content Collections +
astro/zodschema + glob loader,Markdown / MDX - 代码高亮:Shiki(github-light / github-dark 双主题)+ 自定义 transformer 注入标题栏
- 其他集成:
@astrojs/mdx、@astrojs/sitemap、@tailwindcss/typography - 工具库:reading-time(字数/阅读时长)、dayjs(日期格式化)
- 语言:TypeScript(strict)
- 包管理:pnpm
目录结构
astro-blog/ ├── astro.config.mjs # Astro 配置(集成、Shiki、Tailwind) ├── vitest.config.ts # 单元测试配置(node 环境) ├── tsconfig.json # TypeScript(strict)+ 路径别名 ├── src/ │ ├── components/ │ │ ├── home/ # HomeBrand / HomePostCard / FeaturedCard │ │ │ # FeaturedCategories / PostSegments / DotsBand │ │ ├── layout/ # Banner / Waves / Navbar / Footer │ │ ├── post/ # PostCard / PostMeta / PostNavigation / Pagination │ │ └── sidebar/ # ProfileCard / SiteStats / Toc / QuickActions │ │ │ # SeriesPosts / SidebarTabs / Sidebar │ ├── config.ts # 站点配置实例(站点/导航/资料/侧栏/页脚/文章/分类映射) │ ├── config/ │ │ ├── types.ts # 配置类型定义 │ │ └── index.ts # 聚合导出 │ ├── content/ │ │ ├── posts/ # 文章(Markdown/MDX,支持子目录 → 自动派生分类) │ │ └── spec/ # 单页内容(about 等) │ ├── content.config.ts # 内容集合与 frontmatter schema │ ├── layouts/ # BaseLayout(HTML 骨架)/ MainLayout(导航+Banner+网格+侧栏) │ ├── pages/ # 路由:首页 / 文章 / 归档 / 分类 / 关于 / 404 │ ├── plugins/ │ │ └── shiki-code-header.mjs # Shiki transformer:代码块标题栏 │ ├── scripts/ │ │ └── code-copy.ts # 代码块交互:复制 / 全屏 / 折叠 │ ├── styles/ # global.css(配色变量)/ markdown.css(正文排版)/ archive.css │ └── utils/ # content.ts(排序/聚合/统计/分类树)/ cover.ts(封面解析)/ date.ts │ # cover.test.ts(vitest 用例) └── public/ # 静态资源(favicon / iconfont / images)
配置说明
站点配置集中在 src/config.ts,类型定义在 src/config/types.ts:
siteConfig— 站点标题、副标题、语言、Banner(图片/目录/轮播间隔/高度)、每页文章数(pageSize)、图源模式(imageSource:local本地 /cloud云图)、首页布局(homeLayout:single单列 /double双列)、特色页开关(featurePages:about/categories/archive)navConfig— 顶栏菜单项(名称 / 链接 / 图标 / 多级子菜单 / 外链)profileConfig— 侧栏个人卡片(头像 / 昵称 / 简介;可选signatures打字轮播、signatureInterval、social社交链接)sidebarConfig— 侧栏位置(left/right)与包含的组件(profile/stats/toc)footerConfig— 建站年份、备案号、署名作者(默认取profileConfig.name)postConfig— 文章页是否显示字数、阅读时长categoryMap— 分类目录名 → 展示别名映射(分类由文章目录自动派生,此处仅美化展示名)
注意:站点 URL 配置在
astro.config.mjs的site字段(用于 sitemap / canonical),上线前必须改为真实域名。
图片约定
- Banner:
siteConfig.banner.src支持目录自动扫描(/images/banner/,按文件名排序多图轮播)、单图路径或多图数组。 - 头像:
profileConfig.avatar指向目录(如/images/avatar/)时自动取第一张图(云图模式下取 YAMLavatar列表首项);写成显式路径则原样使用。 - 默认封面:文章未指定
image时,从封面图池按文章 key 哈希稳定挑选一张;图池为空则不显示封面区。
图源模式(本地 / 云图)
通过 siteConfig.imageSource 切换 Banner、文章封面与头像的图源,用于把出图流量从本地服务器卸载到 CDN / 对象存储:
local(默认):Banner 扫描public/images/banner/、封面图池取自public/images/cover/、头像取自public/images/avatar/,行为与本地资源一致。cloud:Banner / 封面 / 头像改为读取src/config/images.yaml中的 URL 列表。
src/config/images.yaml 含 banner、cover、avatar 三个键,每个键下一行一个完整 URL:
banner: - https://cdn.example.com/banner/1.webp - https://cdn.example.com/banner/2.webp cover: - https://cdn.example.com/cover/a.webp - https://cdn.example.com/cover/b.webp avatar: - https://cdn.example.com/avatar/me.webp
说明:
- 该 YAML 放在
src/下、仅构建期读取(不会被发布为公开静态资源),构建期经 schema 校验;缺失 / 为空 / 字段非法时安全回退为空图源并告警,不阻断构建。 - 文章 frontmatter 显式指定的
image在两种模式下始终最高优先。 - 是否走云图,取决于对应配置项是否为「目录占位」(以
/结尾):banner.src/profileConfig.avatar写成目录(如/images/avatar/)→ 本地扫目录 / 云图取 YAML 列表;- 写成显式路径或 URL(如
/images/avatar/avatar.jpg、https://...)→ 始终原样使用,即便云图模式也指向该路径(不读 YAML)。 avatar取列表首项。
文章 frontmatter
文章放在 src/content/posts/(支持子目录),schema 定义于 src/content.config.ts:
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 标题(必填) |
published | date | 发布日期(必填) |
updated | date? | 更新日期 |
description | string | 摘要,默认空 |
image | string | 封面图(网络图 / public 绝对路径 / 相对路径),默认空 |
pinned | boolean | 是否置顶,默认 false |
draft | boolean | 是否草稿,默认 false(生产构建时过滤) |
分类不在 frontmatter 中配置,而是由文章所在目录自动派生(一级/多级目录 = 一级/多级分类);顶层文章视为未分类。展示名可通过
categoryMap美化。
文章按「置顶优先 + 发布日期降序」排序,生产环境自动过滤草稿。
已实现特性
- 整站布局:响应式导航栏(多级菜单)、Banner(多图 Ken Burns 轮播 + SVG 海浪过渡)、内容卡片 + 侧栏网格、页脚。
- 侧栏:文章页为「文章目录 / 系列文章 / 站点概览」三标签模块;非文章页为资料卡 + 站点统计 + 社交概览。
- 文章目录(TOC):从最高级标题起的完整层级、CSS counter 层级编号、scroll-spy 高亮当前章节并逐级展开、长标题单行省略。
- 阅读进度:右下角横向悬浮控件「↑ 百分比 ↓」,滚动后淡入。
- 代码块:窗口外壳标题栏(三色圆点 + 语言标签)、复制 / 全屏(弹性缩放动画)/ 超长折叠(箭头跳动提示)。
- 内容页面:首页分页列表、文章详情、归档(年/月)、分类(多级)、关于、404。
- 暗黑模式样式结构:已预留
[data-theme="dark"]覆盖(切换交互待迭代)。 - 阅读时长:≥ 60 分钟自动换算为「N 小时 M 分钟」。
开发与构建
pnpm install # 安装依赖 pnpm dev # 启动开发服务器 pnpm build # 构建静态站点到 dist/ pnpm preview # 本地预览构建产物 pnpm check # astro check 类型检查(strict) pnpm test # vitest 运行单元测试
环境要求:Node ≥ 18,pnpm 11(packageManager 已锁定为 pnpm@11.8.0)。
上线前检查清单
发布到生产环境前,请逐项确认:
- 站点域名:将
astro.config.mjs的site: "https://example.com"改为真实域名(影响 sitemap / canonical)。 - 站点图标:在
public/下放置BaseLayout.astro引用的favicon.svg(当前缺失,会导致图标 404)。 - 个人信息:更新
src/config.ts中的占位社交链接(github.com/、mailto:hi@example.com等)与备案号beian。 - 示例内容:按需移除
src/content/posts/welcome/下的示例文章。 - 图片资源:向
public/images/{avatar,banner,cover}/放入实际图片(目前仅有.gitkeep占位)。 - 质量门禁:
pnpm check/pnpm test/pnpm build三项均通过。
后续迭代规划
- 暗黑模式切换交互(样式结构已预留)
- 站内搜索、评论系统
- 特色页(友链、相册等)
- RSS 输出、更多 SEO 元信息
- 工程化补强:ESLint + Prettier、CI(lint/test/build)、扩充测试覆盖、提取重复的图片扫描工具函数