Bing'Works 这个个人站点断断续续维护了几年。在经历过两次重构之后,我想写一篇文章回顾一下架构上的决策——哪些做对了,哪些是过度设计,以及背后为什么这么权衡。对于一个"小而美"的个人项目,很多决策和公司项目是相反的,这个对比本身就挺有意思。
技术栈全景
┌────────────────────────────────────────────────┐
│ Nginx (静态服务) │
│ / │ \ │
│ index.html css/ js/ │
│ │ │
│ ┌───────┴────────┐ │
│ │ Hash Router │ │
│ │ (纯前端路由) │ │
│ └───────┬────────┘ │
│ │ │
│ ┌─────────┼─────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Works Blog About │
│ (静态) (数据驱动) (静态) │
│ │ │
│ ┌─────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ Markdown 语法高亮 评论系统 │
│ 解析器 渲染器 (GitHub Issues) │
└────────────────────────────────────────────────┘
核心理念:零构建工具、零运行时依赖、零后端服务。 一个 HTML 文件 + CSS + ES Modules,部署就是 scp 到服务器上 Nginx 的静态目录。
为什么不用框架
这个决策每次重构都会被重新审视,但每次都得出同样的结论。
| 维度 | React/Vue/Next.js | 零依赖方案 |
|---|---|---|
| 构建产物 | 50KB-200KB+ JS | ~15KB JS(含所有模块) |
| 冷启动 | 水合、渲染 | 零延迟,HTML 直出 |
| 部署 | 构建流程、CDN | rsync 一行命令 |
| 维护成本 | 依赖升级、安全补丁 | 依赖是浏览器标准 |
对于内容型站点,热更新的价值远不如公司级 SaaS 产品那么大。内容不常变,访问量和交互密度也不高,框架带来的复杂性远超其收益。
但这个决策有一个隐藏成本——生态缺失。Markdown 解析、代码高亮、路由,全得自己写。好在这些模块的单体规模都很小(每个 100-200 行),自己写的代码出了问题能立刻定位,不用翻 GitHub Issues 猜依赖包的 bug。
数据层:无后端不等于无数据
博客文章的数据流设计:
posts/*.md ──→ 用户按需 fetch ──→ 自定义 Markdown 解析器
│ │
▼ ▼
data/posts.json ──→ 页面加载时 fetch ──→ 列表渲染 + 搜索索引
posts.json 是所有文章的元数据(标题、日期、标签、摘要),大约 8KB,页面首次加载就全部拿到,列表渲染和搜索在客户端毫秒级完成——没有网络往返。
文章正文按需加载——用户点击具体文章时才 fetch 对应的 .md 文件,在浏览器端解析渲染。这个策略叫 static + on-demand:列表数据预热,正文懒加载。
几个有意思的细节
主题切换:用 data-theme 属性 + CSS 变量实现。不用 JS 注入样式,而是切换 HTML 属性让所有变量一次性生效。用户的偏好存 localStorage,加载时在 <head> 里同步读避免闪烁。
视图切换:列表视图和卡片视图之间的切换,本质是切换 CSS Grid 的列数和卡片样式。数据层完全不变,只是展示层的 class 变动。这种"纯 UI 状态"不经过路由、不触发数据重新加载。
搜索:全客户端搜索。posts.json 里的摘要已经够用,搜索时对标题和摘要做简单的字符串匹配(考虑中文分词的话会复杂一些,但目前文章量级不需要)。没有后端搜索服务的延迟,打字即时出结果。
踩过的坑
第一次重构时做过的最蠢的事是引入了一个构建工具链——Webpack + Babel + PostCSS。构建时间 15 秒,热更新偶尔失效,配置文件越写越长。一个内容站搞成这样,纯属过度设计。
第二次重构时学乖了,回归"浏览器原生支持什么就用什么"。ES Modules 所有主流浏览器都支持了,CSS 变量和 Grid 在 2020 年就稳定了,Hash Router 不需要 History API 的服务器配置支持。技术选型的金线:先问自己"浏览器能不能直接干"。
最后
这个站点的架构哲学:每一个引入的复杂性,都必须解决一个真实存在且不可回避的问题。 大多数个人站点的问题不是"技术不够先进",而是"技术太厚重,反而拖累了内容本身"。
轻量不等于简陋——它是经过权衡后的精确选择。
评论