架构总览与渲染模型
Mizuki 是一个基于 Astro 的现代化静态博客模板,其架构核心是一条"文章页即规范内容管线"的单次解析渲染模型:Markdown 与 MDX 只解析一次,RSS 与 Atom 订阅源复用同一份 remark/rehype 结果,再做面向 Feed 的静态化、安全清洗与 URL 绝对化。
目的与范围
本页是 Mizuki 架构的顶层参考,覆盖以下内容:
- 系统整体分层:内容层、配置层、Astro 构建渲染管线、静态输出与浏览器端增强;
- 渲染模型的完整机制:单次解析、内容增强集合、Feed 复用与静态降级、白名单清洗与 URL 绝对化;
- 作者侧语法模型:图片增强、Wiki Link 卡片、链接分类的构建期行为;
- 目录组织约定与配置入口(
src/config/、src/content/、src/data/、public/); - 构建期验证与回归夹具(
pnpm test/pnpm build对内容管线的检查)。
以下主题有意留给兄弟页面,本页只做交叉指引:
- 具体写作语法、完整 frontmatter 字段表与发布清单:参见 内容编写指南;
- 内容仓库分离与同步机制:参见 CONTENT_REPOSITORY.md 与 CONTENT_SEPARATION.md;
- 部署平台与托管配置:参见 DEPLOYMENT.md。
说明:本页所有结论均基于仓库根目录 README.md 与 docs/CONTENT_RENDERING.md 中的已验证描述;
src/内部具体函数级实现的逐行分析不在本页阅读范围内展开。
概述
Mizuki 的定位是"静态优先"的博客模板:所有页面在构建期渲染为纯静态 HTML,浏览器端仅保留必要的交互增强。这一决策带来三个直接结果:
- 构建期承担全部内容转换工作。代码高亮(Expressive Code)、数学公式(KaTeX)、图表(Mermaid / PlantUML)、图片优化、Wiki Link 卡片等增强都在 remark/rehype 管线中完成,而不是依赖浏览器端的运行时解析。
- 浏览器端只做"体验层"增强。Swup 负责页面过渡动画,主题切换、壁纸模式、Fancybox 灯箱、目录自动滚动、评论(Twikoo / Giscus)等以渐进增强方式叠加在静态 HTML 之上。
- 搜索与 SEO 也是构建期产物。Pagefind 在构建后生成静态搜索索引;站点地图、robots.txt、RSS、Atom 与可选 Open Graph 图片同样由构建产出。
渲染模型最重要的设计意图是 DRY(Don't Repeat Yourself):文档明确指出"Markdown 与 MDX 只解析一次,RSS 和 Atom 复用相同的 remark/rehype 结果",因此"新增内容语法时,不需要为两个 Feed 分别维护解析器"。这把内容语法的演进成本从"每处输出各改一遍"压缩为"管线内改一处"。
关键术语:
| 术语 | 含义 |
|---|---|
| 规范内容管线 | 文章页渲染所使用的完整 remark/rehype 解析与增强流程,是所有内容转换的唯一权威来源 |
| Feed 静态降级 | 将交互性内容转换为 Feed 可用的静态形式(如展开代码组、移除事件属性)的过程 |
| 白名单清洗 | Feed HTML 只允许显式声明的标签与属性通过的 安全策略 |
| URL 绝对化 | 将文章内相对链接、public 图片、Astro 构建图片与 srcset 转换为绝对 URL |
| Wiki Link | [[post]] 形式的站内引用语法,独立成行时生成目标文章卡片 |
系统架构
下图展示 Mizuki 从内容源到最终浏览器呈现的整体结构。所有组件名称均为仓库文档中真实存在的技术选型与目录约定:
分层说明
内容源层。Mizuki 把内容按形态拆分到四个位置:文章(src/content/posts/ 下的 .md / .mdx)、专页文本(src/content/spec/,如关于页与友链页)、结构化数据(src/data/,如项目、技能、设备、时间线、AI 工具、追番、日记、相册等特色页面的数据源)与公共资源(public/)。这种拆分让"写文章"与"维护结构化数据"互不干扰。
单次解析层。remark / rehype 是整个渲染模型的中枢。README 列出的全部 Markdown 扩展能力(提示框、KaTeX、Expressive Code、Mermaid、PlantUML、GitHub 卡片、Wiki Link、剧透、响应式图片、图片网格、Fancybox 灯箱、HTML 嵌入)都在这一层被转换或标记,之后文章页与两个 Feed 共享同一份结果。
静态输出层。文章页 HTML 是"规范输出";RSS 与 Atom 不是重新解析 Markdown,而是对文章页结果的复用。这一层还包括 Pagefind 索引与 SEO 产物(站点地图、robots.txt、Open Graph 图片)。
浏览器端层。Swup 提供无刷新的页面过渡;主题切换(含系统偏好检测)、可切换壁纸模式(透明度与模糊可调)、Fancybox 灯箱、交互式目录(自动滚动)、评论系统与可选的 Live2D 看板娘都工作在这一层。由于页面本体是静态 HTML,这些增强失效时内容依然可读。
核心流程:单次解析的渲染管线
下图按真实执行顺序展示一篇文章从 Markdown 源文件到三类输出(文章页、RSS、Atom)的完整数据流。关键约束是:remark/rehype 解析与内容增强 只发生一次,Feed 分支在其后:
各阶段要点
阶段一:frontmatter 门控。title 与 published 是必填字段;其余字段(摘要、图片、标签、分类、草稿、置顶、评论、别名、固定链接、署名、加密)均为可选。加密文章在进入管线前就被特殊处理:它们 不进入 RSS 和 Atom,且不会在文章列表、文章页或 Wiki Link 卡片中暴露封面。
阶段二:单次解析与增强。所有内容增强在这一步集中完成。值得注意的是 README 中关于 PlantUML 的安全约束:默认使用 src/config/markdownConfig.ts 中配置的 公共服务器 渲染,因此"请勿在图表中写入密码、Token 或隐私数据"——这是一个把数据外发风险显式写进文档的设计边界。
阶段三:Feed 静态降级。这是渲染模型中最具工程价值的部分。降级规则包括:
- 保留:Callout 标题、Wiki Link 摘要与封面、MathML(KaTeX 的无脚本输出形式)、代码组标签;
- 展开:代码组被展平为静态内容;
- 移除:交互脚本与事件属性;
- 转换:文章内相对链接、public 图片、Astro 构建图片与
srcset全部转为绝对 URL。
清洗使用 显式标签/属性白名单(而非黑名单),这保证了 Feed 内容的最小攻击面;最后 RSS 与 Atom XML 都经过 严格解析验证,确保下游阅读器不会因格式错误而解析失败。
作者语法在构建期的行为
图片增强遵循"alt 只做无障碍、title 做说明"的分离原则:
Source: CONTENT_RENDERING.md
规则细节:w-N% 必须在 1 到 100 之间,有效标记会从 alt 中移除并设置显示宽度,无效值会原样保留(不会静默丢弃,作者可以看到原文)。图片默认启用懒加载与异步解码。匹配 siteConfig.imageOptimization.noReferrerDomains 的远程图片会在构建 HTML 中直接得到 referrerpolicy="no-referrer"——文档强调这样做是为了"避免首次请求已经携带 Referer",即在元素加载前就把隐私策略固化到 HTML,而不是等脚本事后修补。
防止重复包装的豁免清单同样明确:图片网格、Wiki Link 封面、图表、已有 figure,以及带 data-no-enhance 的容器 不会被重复包装。data-no-enhance 因此成为作者对构建期增强行为的手动逃生舱。
链接分类在页面与 Feed 之间共享同一份结果:相对链接、片段和当前站点的绝对 URL 都按站内链接处理;真正的外链沿用 _blank 与 nofollow noopener noreferrer 兼容策略。文档同时指出"更细的 rel 策略和邮箱保护属于独立链接策略功能",明确了渲染模型与链接策略模块的边界。
目录组织与配置模型
Mizuki 的配置与内容按职责严格分目录。下表是 README 快速开始章节给出的权威约定:
| 路径 | 角色 | 典型内容 |
|---|---|---|
src/config/siteConfig.ts | 站点主配置 | siteURL(部署前必须替换)、横幅轮播、壁纸、imageApi、imageOptimization.noReferrerDomains 等 |
src/config/(其他模块) | 功能模块配置 | 如 markdownConfig.ts 中的 PlantUML 公共服务器配置 |
src/content/posts/ | 文章 | .md 与 .mdx,title / published 必填 |
src/content/spec/ | 专页文本内容 | 关于页、友链页等 |
src/data/ | 结构化页面数据 | 项目、技能、设备、时间线、AI 工具、追番、日记、相册 |
public/ | 公共静态资源 | 使用 /images/example.webp 形式的根路径引用 |
.env(根目录) | 环境变量 | ENABLE_CONTENT_SYNC=false、Bilibili 会话、IndexNow 凭据等 |
图片引用遵循两条路径规则:文章本地图片放在文章旁边,用 ./cover.webp 这样的 相对路径 引用(并进入 Astro 图片优化);公共图片放在 public/ 下,用 /images/example.webp 这样的 根路径 引用。这一区分直接决定了构建期图片优化的作用范围——只有相对路径的本地图片才会生成 160、320、480 像素的响应式缩略图。
Wiki Link 卡片的封面解析复用同一套路径语义,按优先级处理四种形态:./cover.webp 相对目标文章文件解析并进入 Astro 图片优化;/images/cover.webp 作为 public 路径;https://... 作为远程图片;image: api 使用 siteConfig.banner.imageApi 返回的图片列表并 按文章稳定选择(同一文章总是得到同一张图)。缺图或 API 失败时显示无图卡片,这是显式的失败降级路径。
失败模式与边界情况
基于文档中明确声明的边界,渲染模型存在以下已验证的失败模式与处理方式:
| 场景 | 行为 | 设计意图 |
|---|---|---|
w-N% 值越界(不在 1–100) | 标记原样保留在 alt 中 | 不静默修改作者内容,错误可见 |
远程图片命中 noReferrerDomains | 构建 HTML 中直接输出 referrerpolicy="no-referrer" | 在请求发出前固化隐私策略,而非事后修补 |
Wiki Link 目标缺图或 imageApi 失败 | 显示无图卡片 | 保证卡片布局不因数据缺失而破碎 |
| 文章启用加密 | 不进入 RSS / Atom;列表、文章页、Wiki 卡片不暴露封面 | 浏览器端加密不是服务端访问控制,Feed 是公开通道 |
| PlantUML 图表 | 默认通过公共服务器渲染 | 显式提示不要在图表中写入密码、Token 或隐私数据 |
| Feed 中的交互内容 | 代码组展开、交互脚本与事件属性移除 | 阅读器环境无法承载脚本,必须静态化 |
| Feed HTML 安全 | 显式标签/属性白名单清洗 | 白名单优于黑名单,最小化攻击面 |
| Feed XML 合法性 | RSS 与 Atom 都经过严格解析验证 | 保证下游阅读器解析成功 |
值得强调的是加密内容的边界处理:README 明确指出"浏览器端加密不是服务端访问控制"(README L150)。也就是说,加密文章的 HTML 仍然会随静态站点一起分发,安全性依赖浏览器端的解密交互;正因如此,渲染模型选择把加密内容从 RSS / Atom 这两个"必然以明文聚合分发"的通道中整体剔除,而不是仅仅加密正文。
构建期验证与质量保障
渲染模型的正确性由仓库内建的验证机制保障。docs/CONTENT_RENDERING.md 声明 src/content/posts/content-pipeline-fixture.mdx 是"面向用户的完整示例,也是构建回归夹具",并给出四条验证命令:
1pnpm test
2pnpm check
3pnpm type-check
4pnpm buildSource: CONTENT_RENDERING.md
其中 pnpm build 会额外检查夹具页面、RSS 和 Atom 的 静态内容、XML、安全属性、绝对 URL、Wiki 封面和链接分类是否一致。这实际上把渲染模型的所有关键不变量(单次解析的一致性、白名单清洗的正确性、URL 绝对化的完整性、链接分类的一致性)都变成了构建期的断言,任何一条管线回归都会导致构建失败——"夹具即规格"的做法让文档描述的渲染契约可以被机器持续验证。
日常开发命令(来自 README 命令表)也围绕这一管线组织:pnpm dev 启动本地开发服务器(http://localhost:3000),pnpm new-post -- <文件名> 创建新文章,部署目标覆盖 Vercel / Netlify / GitHub Pages / Cloudflare Pages。
相关链接
- README.md —— 功能特性总览、快速开始、命令表
- docs/CONTENT_RENDERING.md —— Markdown 内容渲染的权威文档(本页渲染模型的主要依据)
- docs/CONTENT_AUTHORING.zh.md —— 内容编写指南(完整字段表与写作语法,兄弟页面)
- docs/CONTENT_REPOSITORY.md —— 内容仓库机制(兄弟页面)
- docs/CONTENT_SEPARATION.md —— 内容分离策略(兄弟页面)
- docs/DEPLOYMENT.md —— 部署指南(兄弟页面)
- docs/AUTO_BUILD_TRIGGER.md —— 自动构建触发说明