跨框架 Astro 项目模板
跨框架 Astro 项目模板
最近完成了一个暂时叫作 astro-temp 的项目。
它不是一个新的博客主题,也不是把现有博客换个目录重新复制一遍。它更接近一套面向个人项目的 Web 基础架构:以后做作品展示站、图集浏览站、游戏论坛、网页游戏入口、文档站或博客时,都可以从同一个起点开始。
这个起点需要解决的不是某一种网站的业务,而是不同网站都会重复遇到的问题:
- 页面如何保持同一套视觉语言。
- Astro、Vue 和原生组件如何在一个项目里共存。
- 多语言、请求、主题、SEO 和页面过渡如何统一。
- 文档、新闻、公告和文章如何作为可选内容能力保留下来。
- 搜索、评论、音乐、Live2D、Spine 等能力如何按需开启。
- 新项目如何避免再次复制一批互相耦合的旧代码。
这篇文章记录为什么要做这个模板、目前做了什么,以及这些能力是怎样组织起来的。
起点:不想让每个网站重新长一遍
在只有一个博客时,很多问题都可以直接在博客里解决。
Header、Footer、首图、主题切换、文章卡片、评论、音乐播放器,只要能服务当前站点就够了。随着想做的网站越来越多,这种做法开始出现明显问题:
- 每个项目都会重新写一遍基础布局。
- 相同按钮、卡片和状态提示在不同项目中逐渐产生细微差异。
- Vue 项目里做好的组件不能直接放进 Astro 的 Markdown 正文。
- 博客里的代码带着很强的内容结构,直接复制到作品站会显得像“换了文字的博客”。
- 后续修复主题、移动端或可访问性问题时,需要在多个仓库重复修改。
最初的想法确实是“参考原博客,把已有样式搬过来”。但做到侧边栏、内容路由和文章功能后,很快就能感觉到方向不对:如果继续按照页面逐个复刻,最后得到的只会是另一份博客源代码,而不是可以搭建不同网站的模板。
真正需要提取的不是某个页面,而是页面背后的规则。
例如:
- Header 应该提供配置和插槽,而不是写死博客菜单。
- Footer 应该支持备案、版权、作者、自定义行和双列布局,而不是绑定当前站点信息。
- 首图应该是页面壳层的一部分,并允许首页和内页使用不同高度。
- 侧边栏应该是布局能力,作者卡片、分类和标签只是默认小组件。
- 发布系统应该能承载新闻、公告、文章和更新日志,但不应该成为所有网站的强制首页。
这次调整之后,项目的目标才逐渐明确:保留同一种设计语言,但不预设网站最终长什么样。
模板的定位和边界
astro-temp 的定位可以分成两层。
第一层是所有项目都会使用的基础壳层:
- Astro 静态页面和通用布局。
- 站点身份、主题、Header、Footer 和页面首图配置。
- Astro、Vue 与 Web Components 共用的主题变量。
- 服务端和客户端都能使用的 i18n。
- 统一的 Axios 请求客户端与 Toast 桥接。
- SEO、Sitemap、robots、OpenGraph 和基础页面过渡。
第二层是按项目选择的 Feature:
- 发布内容与 RSS。
- 文档路由。
- 侧边栏和阅读目录。
- 静态搜索。
- 图片灯箱。
- 评论适配器。
- Mermaid 和 KaTeX。
- 字体与列表布局偏好。
- 音乐、Live2D 和 Spine。
这里的“可选”不仅表示界面上可以隐藏,还意味着关闭后不应该继续加载对应运行时、模型或第三方资源。
最终形成的是一个共享基础,而不是一个必须打开全部功能的成品站。
做了什么
一套跨框架的视觉基础
模板依赖三个已经独立发布的包:
@mar7th/march7th-theme提供颜色、状态色、圆角、阴影和明暗模式等 Design Tokens。@mar7th/march7th-ui提供原生m7-*Web Components。@mar7th/march7th-ui-vue提供适合 Vue 项目和 Vue Island 使用的组件。
Astro 页面、Vue 组件和原生组件不再各自维护一套主题。它们都读取同一组 CSS 变量,并由同一个主题控制器处理模式和配色。
模板内部另外定义了三个通用圆角层级:
:root { --radius-large: 1rem; --radius-medium: 0.75rem; --radius-small: 0.5rem;}大卡片、中等内容块、代码框和小型控件由明确层级控制,不再在每个组件里散落不同的像素值。

截图中的页面主体由 Astro 输出静态 HTML,交互区域只在进入视口后挂载 Vue,原生 m7-* 标签则由浏览器升级为 Custom Elements。三种实现方式可以并存,但用户看到的是同一套颜色、间距和交互语言。
可配置的站点壳层
Header 和 Footer 最初参考了原博客的样式与交互,后来把站点相关内容全部改成配置和插槽。
Header 支持:
- 普通链接与 hover 下拉菜单。
- 图标、外链和选中规则。
- 语言、主题与配色按钮的独立开关。
- 滚动到内容区域后隐藏。
- 左、中、右三列插槽覆盖。
Footer 支持:
- 备案信息、版权、作者、RSS 和 Powered by 的独立开关。
- 一组可直接渲染的自定义行。
- 单列居中和桌面端一比二的双列布局。
- 右列插槽以及移动端上下排列。
页面首图也被整理成统一壳层。首页使用约 65vh 的沉浸式首图,内页使用一半高度,并通过波浪过渡与主体连接。这样新页面只需要选择 home 或 compact 模式,不需要重新实现 Header、首图和内容之间的关系。
发布能力不是博客专属能力
文档、新闻和站点内容仍然是模板的重要组成部分。
即使最终项目是作品站、图集站或游戏入口,也常常需要公告、开发日志、更新记录、帮助文档和活动信息。因此模板保留了一个中性的 Publishing Feature,而不是把它命名为 Blog。
目前发布能力包含:
- Markdown 与 MDX 内容集合。
- 新闻、公告、文章、更新日志和活动等内容类型。
- 分页、分类、标签和归档。
- 多语言 RSS。
- 字数、阅读时间、上一篇和下一篇。
- 首页最新内容。
- 侧边栏作者、分类、标签和最近内容组件。
- 随机封面、自定义封面与失败回退。
- 图片灯箱、代码复制和文章目录。

随机封面使用文章的稳定标识生成候选地址,因此列表和正文会得到同一张图。自定义图片则直接使用内容配置。随机图会显示 Random Cover 提示,自定义封面不显示;打开文章后,封面和正文图片都可以进入 PhotoSwipe 灯箱。
发布内容放在:
src/content/publishing/├── zh/└── en/列表、详情、分类、标签、归档和 RSS 都从同一份内容集合生成,不需要在首页再维护一份文章数据。
文档有独立的路由边界
普通页面使用统一动态入口,文档则明确放在 /docs/ 下:
src/content/docs/├── zh/└── en/这样文档的 catch-all 路由不会与作品页、活动页或其它业务页面冲突。Markdown 和 MDX 可以继续享受内容集合校验、目录、代码高亮、图片灯箱和原生组件能力。

模板没有额外实现一套 Admonition 语法,因为 Markdown 中已经可以直接使用 m7-alert、m7-notice 等原生组件。Mermaid 和 KaTeX 保留为可选能力,并默认关闭。
多语言只有一份页面实现
如果中英文页面只差一个 locale,就没有必要在 pages/ 下维护两份相同文件。
模板使用统一页面声明和 getStaticPaths() 生成实际存在的语言路由:
- 中文默认不带前缀。
- 英文使用
/en/。 - 页面组件始终显式接收 locale。
- 翻译词条按语言目录组织。
- 只有中文的页面仍会生成英文访问入口,并重定向回中文。
这里没有依赖 Astro 内置 fallback 去猜测动态路由是否存在,而是在构建静态路径时根据真实页面和内容集合生成回退关系。
服务端翻译也不会修改全局当前语言。每次调用都显式传入 locale,避免静态构建或服务端并发渲染时互相污染语言状态。
页面过渡与客户端生命周期
页面过渡默认开启,但不能简单地让整个页面同时淡出再淡入。
模板保留 Header 等全局壳层,只更新主要内容,并把离开和进入动画统一成:
- 旧内容轻微向下并渐隐。
- 新内容从下方向上进入并渐显。
- 主体和页面专属侧边栏使用同一种节奏。
- 减少动态效果的系统设置会缩短或关闭动画。
这部分不仅是 CSS 问题。Vue Island、原生组件、评论、音乐和看板娘都需要正确处理页面切换后的初始化与清理。评论适配器会保存并调用 cleanup;音乐、Live2D 和 Spine 则由各自模块管理资源与生命周期。
搜索、SEO 和部署路径
模板以静态输出为默认模式:
- Pagefind 在构建结束后生成搜索索引。
- Sitemap、robots 和多语言 RSS 在构建期生成。
- OpenGraph 图片可以按站点和内容生成。
- canonical、JSON-LD 和语言链接由统一站点配置输出。
PUBLIC_BASE_PATH同时控制 Astro base、资源、导航、RSS、搜索和 Markdown 根路径。
这意味着项目可以部署在域名根目录,也可以部署到 /blog/、/docs/ 之类的子路径,而不是只修改一个链接后让其它静态资源继续指向根目录。
怎么组织这些能力
配置只负责策略
所有项目级策略集中在 src/config/:
src/config/├── site.ts # 站点身份、资源、SEO 与主题默认值├── header.ts # 导航结构├── footer.ts # 备案、版权和自定义行├── features.ts # 可选能力总开关├── publishing.ts # 发布路径、分页和分类能力├── sidebar.ts # 侧边栏位置和小组件├── cover.ts # 随机封面 API 与 fallback├── music.ts # 音乐来源└── mascots.ts # Live2D 与 Spine组件负责渲染,Feature 负责完整业务能力,配置只描述项目要选择什么。这样不会出现关闭一个功能后还需要去多个页面手动删除入口的问题。
Feature 拥有自己的文件
可选能力都提升为一级 Feature,并保留在自己的模块内:
src/features/├── publishing/├── reading/├── search/├── comments/├── seo-og/├── preferences/├── music/├── live2d/└── spine/这种结构为后续 CLI 做准备。当前版本先作为包含全部能力的完整初始包发布,后续 CLI 可以根据选择复制 Feature 文件、公共资源和配置片段,再执行少量可预测的入口修改。
静态优先,交互按需加载
首页、Header、Footer、首图、卡片、文档和发布列表都是 Astro 组件,默认直接输出 HTML。
需要交互时再选择运行时:
- Vue 示例使用
client:visible。 - Toast Host 在真正需要的页面加载。
- 原生组件只在页面请求对应运行时时注册。
- PhotoSwipe 在用户点击图片时动态导入。
- Pagefind 在输入搜索词后加载。
- Mermaid 只在存在 Mermaid 代码块时加载。
- 音乐和模型 Feature 关闭时不请求运行时与资源。
模板不是为了追求“完全没有 JavaScript”,而是让每一段 JavaScript 都有明确用途。
它现在可以拿来做什么
当前版本直接拿来做博客没有问题,发布、分类、标签、归档、RSS、目录和评论接口都已经具备。
但它不只适合博客:
- 作品展示站可以关闭 Publishing,保留统一壳层和项目自定义页面。
- 图集站可以复用内容布局、筛选、灯箱和对象存储资源。
- 游戏论坛可以使用 SSO、发布能力和项目自己的社区后端。
- 网页游戏入口站可以使用卡片、公告、活动和统一身份。
- 产品或开源项目可以直接使用 Docs、搜索和更新日志。
不同站点应该有自己的信息架构和主要工作流。模板只负责让它们共享同一种基础质量和视觉身份,而不是让所有站点看起来一模一样。
还没有做完的部分
目前 astro-temp 是一个“完整能力版本”,不是最终的 CLI 产物。
下一阶段需要继续处理:
- 明确最小本体的文件清单。
- 为每个 Feature 建立依赖、资源和入口修改清单。
- 支持创建项目时选择功能。
- 支持在已有项目中追加 Feature。
- 处理 Feature 之间的依赖和冲突检测。
- 把示例品牌与受版权约束的资源替换为适合分发的默认内容。
先完成一个包含全部能力、可以真实运行和构建的版本,再从真实边界拆分 CLI,比一开始只设计一套看似整洁的文件生成规则更可靠。
总结
做这个模板的核心原因,是不想再用复制项目的方式保持一致。
主题库解决视觉变量,组件库解决跨项目控件,Astro 模板解决页面壳层、内容、路由和工程能力。三者组合以后,新项目可以从统一基础上直接进入业务设计,而不是先花一周重新处理主题、Header、Footer、多语言、请求和文章系统。
它仍然会继续变化,但方向已经确定:
让不同类型的网站共享设计语言、基础能力和工程质量,同时保留各自真正需要的页面结构与产品体验。
参考资料
主题库和组件库的设计细节、分层方案与当前成果,单独整理在下面两篇文章中:
March7th