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/zod schema + 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)、图源模式(imageSourcelocal 本地 / cloud 云图)、首页布局(homeLayoutsingle 单列 / double 双列)、特色页开关(featurePagesabout / categories / archive
  • navConfig — 顶栏菜单项(名称 / 链接 / 图标 / 多级子菜单 / 外链)
  • profileConfig — 侧栏个人卡片(头像 / 昵称 / 简介;可选 signatures 打字轮播、signatureIntervalsocial 社交链接)
  • sidebarConfig — 侧栏位置(left / right)与包含的组件(profile / stats / toc
  • footerConfig — 建站年份、备案号、署名作者(默认取 profileConfig.name
  • postConfig — 文章页是否显示字数、阅读时长
  • categoryMap — 分类目录名 → 展示别名映射(分类由文章目录自动派生,此处仅美化展示名)

注意:站点 URL 配置在 astro.config.mjssite 字段(用于 sitemap / canonical),上线前必须改为真实域名

图片约定

  • Banner:siteConfig.banner.src 支持目录自动扫描(/images/banner/,按文件名排序多图轮播)、单图路径或多图数组。
  • 头像:profileConfig.avatar 指向目录(如 /images/avatar/)时自动取第一张图(云图模式下取 YAML avatar 列表首项);写成显式路径则原样使用。
  • 默认封面:文章未指定 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.yamlbannercoveravatar 三个键,每个键下一行一个完整 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.jpghttps://...)→ 始终原样使用,即便云图模式也指向该路径(不读 YAML)。
    • avatar 取列表首项。

文章 frontmatter

文章放在 src/content/posts/(支持子目录),schema 定义于 src/content.config.ts

字段类型说明
titlestring标题(必填)
publisheddate发布日期(必填)
updateddate?更新日期
descriptionstring摘要,默认空
imagestring封面图(网络图 / public 绝对路径 / 相对路径),默认空
pinnedboolean是否置顶,默认 false
draftboolean是否草稿,默认 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.mjssite: "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)、扩充测试覆盖、提取重复的图片扫描工具函数