Repository Wiki
LyraVoid/Mizuki

架构总览与渲染模型

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 对内容管线的检查)。

以下主题有意留给兄弟页面,本页只做交叉指引:

说明:本页所有结论均基于仓库根目录 README.md 与 docs/CONTENT_RENDERING.md 中的已验证描述;src/ 内部具体函数级实现的逐行分析不在本页阅读范围内展开。

概述

Mizuki 的定位是"静态优先"的博客模板:所有页面在构建期渲染为纯静态 HTML,浏览器端仅保留必要的交互增强。这一决策带来三个直接结果:

  1. 构建期承担全部内容转换工作。代码高亮(Expressive Code)、数学公式(KaTeX)、图表(Mermaid / PlantUML)、图片优化、Wiki Link 卡片等增强都在 remark/rehype 管线中完成,而不是依赖浏览器端的运行时解析。
  2. 浏览器端只做"体验层"增强。Swup 负责页面过渡动画,主题切换、壁纸模式、Fancybox 灯箱、目录自动滚动、评论(Twikoo / Giscus)等以渐进增强方式叠加在静态 HTML 之上。
  3. 搜索与 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 从内容源到最终浏览器呈现的整体结构。所有组件名称均为仓库文档中真实存在的技术选型与目录约定:

Loading diagram...

分层说明

内容源层。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 分支在其后:

Loading diagram...

各阶段要点

阶段一: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 做说明"的分离原则:

markdown
![架构图 w-75%](./architecture.webp "统一内容管线")

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 是"面向用户的完整示例,也是构建回归夹具",并给出四条验证命令:

bash
1pnpm test 2pnpm check 3pnpm type-check 4pnpm build

Source: 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。

相关链接

Sources

(2 files)