本站技术架构:从 Rust WASM 到 Astro 全栈
本文另有英文版本 — 阅读 English 版本
概览
这个站点是一个完全静态生成(SSG)的个人门户,包含札记、工具和关于页。技术栈如下:
| 层 | 选型 |
|---|---|
| 框架 | Astro 7(静态生成) |
| 交互 | SolidJS(客户端水合) |
| 样式 | Tailwind CSS 4 + 语义 Token |
| 运行时 | Bun(构建、包管理、脚本) |
| 高性能计算 | Rust → WASM |
| 类型约束 | TypeScript + Zod |
Monorepo 分层架构
项目使用 Bun workspaces 管理三个内部包 + 一个应用:
packages/
ui/ → 设计系统原语(Button, Icon, Prose...)
content/ → Zod schema + 领域类型
wasm/ → Rust 编译的 WASM 模块
apps/
i/ → Astro 站点
依赖方向严格单向:
pages(薄路由)
└── features/*(home / essays / tools / about)
└── shared / i18n / config / lib(跨功能共享)
└── packages/ui | content | wasm
features 之间禁止互相 import。组件访问 features 一律走 barrel export(index.ts),内部结构对外不可见。
静态生成 + i18n 路由
Astro 配置 output: "static",所有页面在构建时预渲染为纯 HTML。i18n 使用文件路由:
pages/
index.astro → / (默认 zh)
about.astro → /about/
en/ → /en/ 前缀
index.astro → /en/
about.astro → /en/about/
每个页面文件是 3-15 行的薄代理,真正的组件和逻辑都在 features/ 中:
---
import { AboutPage } from "@/features/about";
import { asLocale } from "@/i18n";
---
<AboutPage locale={asLocale(Astro.currentLocale)} />
i18n 字典采用 type-safe 模式:zh.ts 定义 Messages 类型,en.ts 受其约束。任何新增 key 如果漏翻译会在编译期报错。
SolidJS + client:load 水合
10 个在线工具(JSON 格式化、Base64 编解码、哈希计算器、二维码生成器等)全部是 SolidJS 组件,通过 Astro 的 client:load 指令进行客户端水合。
一个关键细节:Astro 的水合要求组件必须是字面 JSX 标签(<JsonFormatter client:load />),不能通过变量或对象映射动态引用。因此 ToolPage.astro 中为每个工具保留了静态分支,并于 registry.ts 中的 toolIds 数组保持同步。
工具共享组件(InputCard、OutputCard、CopyButton)放在 features/tools/shared/,遵循“不到 2 处复用不放入 packages/ui”的原则。
Rust → WASM:哈希计算器
哈希计算器(SHA-256、SHA-512、MD5、SHA-1、BLAKE3)的后端是 Rust 编译的 WASM。
packages/wasm/native/src/crypto.rs 使用 sha2、md-5、blake3、sha1 crate 实现五个哈希函数,通过 #[no_mangle] 导出为 C ABI:
#[no_mangle]
pub extern "C" fn sha256(ptr: *const u8, len: usize, out: *mut u8) {
let data = unsafe { std::slice::from_raw_parts(ptr, len) };
let hash = Sha256::digest(data);
unsafe { std::ptr::copy_nonoverlapping(hash.as_ptr(), out, 32) };
}
TypeScript 侧通过 packages/wasm/src/loader.ts 加载并封装为类型安全的 WasmTools 接口。构建脚本 bun scripts/build.ts 调用 cargo build --target wasm32-unknown-unknown 并将产物复制到 dist/tools.wasm。
Tailwind CSS + 语义 Token
不使用 dark: 双写,而是基于 CSS 自定义属性的语义 token 体系。所有颜色定义集中在 packages/ui/src/styles/theme.css:
:root {
--color-bg: #fff;
--color-ink: #111;
--color-ink-muted: #555;
--color-accent: #2563eb;
}
.dark {
--color-bg: #111;
--color-ink: #eee;
--color-accent: #60a5fa;
}
组件层只用语义类名(bg-surface、text-ink、border-line),绝不直接写 zinc-*。换肤只需改一个文件。
暗色模式
暗色模式通过 <script is:inline> 在 <head> 中内联执行,阻止 FOUC(闪白):
const stored = localStorage.getItem("theme");
const dark = stored
? stored === "dark"
: matchMedia("(prefers-color-scheme: dark)").matches;
document.documentElement.classList.toggle("dark", dark);
主题切换由 ThemeToggle.tsx(SolidJS island)控制,同步更新 localStorage 和 DOM class。同时监听 astro:after-swap 事件兼容 Astro 视图过渡。
内容管理
札记使用 Astro Content Collections,通过 Zod schema 校验 frontmatter:
const postSchema = z.object({
lang: z.enum(["zh", "en"]),
title: z.string(),
pubDate: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
sameAs: z.string().optional(), // 跨语言互链
});
所有数据访问统一经 features/essays/data.ts,换 CMS 只改一个文件。卡片渲染、RSS 生成、标签聚合都调用同一组数据函数。
构建与部署
bun run build # astro build → dist/ (40 页,~1.5s)
构建产出纯静态文件,零 JS 服务端依赖。@/ 路径别名由 tsconfig.json 的 paths 和 Astro 的 Vite alias 共同保障,消除所有 ../../../../ 深度相对导入。
小结
这个站点虽小,但覆盖了现代 Web 开发的多个关键领域:SSG 架构、i18n 路由、前端水合、WASM 计算、设计 token 系统、内容型 schema 校验。每一层的选型都遵循“够用但不简陋”的原则,没有为 future-proof 预留过度抽象。
后续计划
当前站点是纯静态生成,所有内容以 Markdown 文件管理。下一阶段规划了几项关键演进:
数据库接入。 将札记、标签、翻译映射从文件系统迁移到数据库(SQLite 或 PostgreSQL),支持更灵活的查询和关联。工具的用户偏好(收藏、历史记录)也需要持久化存储。
Dashboard 后台。 搭建管理后台,提供可视化的札记编辑器、标签管理、翻译关联面板。接口层面设计 RESTful API,前台和后台共享同一数据层。
在线编辑与发布流程。 在 Dashboard 中实现 Markdown 实时预览、草稿/发布状态切换、定时发布。
自动化部署。 站点的前后端代码通过 Webhook 在 Git 推送后自动拉取并部署更新。
这些方向不追求一步到位,而是按实际需求逐步接入,保持架构的可演进性。
系列文章 · site-build