做个人博客最让人上头的不是写了几篇文章,而是每次打开首屏,白屏时间从两秒多被压到零点几秒的那种爽感。我前前后后折腾过不少博客方案:WordPress 功能全面但身子太重,Hexo 插件丰富但依赖链太长,换台电脑就要重新折腾 Node 环境,后来在一个叫 colibri 的项目里找到了平衡点。这个名字源自法语和西班牙语里的“蜂鸟”,我当时的诉求很明确——做一个加载极快、结构清晰、让我愿意持续更新的轻量级博客主题,不堆砌功能,不搞花哨动效,把每一毫秒都花在刀刃上。这篇文章主要记录 colibri 从立项、改名、技术选型到落地优化的完整过程,适合那些正在纠结“博客到底该用什么技术栈”“怎么把站点做得足够快”的朋友参考,也适合单纯想看看一个个人项目能怎么一步步打磨的人。
1. 为什么用蜂鸟命名:从产品定位倒推技术决策
1.1 蜂鸟的三个特征,对应三种产品原则
蜂鸟这种生物很特别,它体型极小,却能悬停在空中,翅膀每秒能扇动几十次,飞行方向灵活到可以倒着飞。如果给我的博客主题提炼三个关键词,我会选“轻量、敏捷、精准停留”,这正好和蜂鸟的习性对上了。
- 轻量:不引入无谓的依赖,整个站点只输出必要的 HTML、CSS 和少量 JavaScript,拒绝“为了用框架而用框架”。
- 敏捷:内容从写完到上线要足够快,本地预览要快,构建要快,部署也要快,不让人在等待中磨掉写作热情。
- 精准停留:读者进来之后,能立刻看到文章的核心内容,不被导航、弹窗、分享按钮干扰,注意力精准地落在文字上。
最初这个项目其实不叫 colibri,我用的代号是 super-blog,跟所有烂大街的模板项目一样,毫无记忆点。后来做页面性能分析的时候,看到蜂鸟扇动翅膀的高频视频,突然意识到,我要做的不是一只“更大的鸟”,而是一只“更快的鸟”,于是果断改了名。事实证明,一个好名字能给项目带来方向感。每次我在功能取舍上犹豫的时候,就会问自己一句“蜂鸟会这么干吗”,大部分时候答案都很明确。
1.2 明确用户画像,避免功能蔓延
产品定位清楚之后,还要想清楚服务的人群。我做 colibri 不是为了服务“所有人”,而是服务三类人:一是像我一样每天写技术笔记的开发者,二是偶尔写长文但不想折腾建站的独立创作者,三是只阅读不写作的访客。
这三类人的需求矛盾其实不小。写作者希望发布流程简单,创作者希望排版好看,阅读者希望加载快、不被打扰。很多博客平台最后做得又重又乱,就是因为想同时讨好所有人,结果每个角色都很不满意。所以我给 colibri 定了一个非常刻薄的功能边界:凡是需要在页面里写超过二十行 JavaScript 才能实现的功能,一律不做;凡是能通过 CSS 优雅解决的效果,绝不动用脚本;凡是与阅读无关的模块,默认不放在首屏。
这套取舍原则导致 colibri 的功能列表短得可怜:博客列表、文章页、归档、关于页、RSS 订阅,加起来就这几样。评论区暂时不做,搜索暂时不做,多语言暂时不考虑。但我可以负责任地说,对于一个以内容为核心的博客,这套功能已经是够用的,而且因为砍掉了那些沉重的模块,整个站点的加载速度提升了不止一个档次。
2. 技术选型的真实理由:在速度与维护成本之间做取舍
2.1 静态站点生成器对比:我为什么选了 Hugo
静态站点生成器(SSG)是个人博客的主流选择,市面上比较常见的是 Hugo、Jekyll、Hexo、VitePress,我在确定方案之前把这几个都摸了一遍,列了一张对比表:
| 方案 | 运行时 | 构建速度 | 依赖复杂度 | 模板引擎 | 适合场景 |
|---|---|---|---|---|---|
| Hugo | Go 编译出的单二进制 | 极快,千篇文章秒级 | 几乎为零 | Go Template | 追求性能、内容量大的站点 |
| Jekyll | Ruby | 中等,文章多了会慢 | 较多,依赖容易冲突 | Liquid | GitHub Pages 原生支持 |
| Hexo | Node.js | 中等 | 多,npm 依赖链长 | EJS/Swig | 插件生态丰富 |
| VitePress | Node.js | 快,但构建消耗更高 | 中等 | Vue SFC / Markdown | 文档站、喜欢 Vue 生态 |
我最后选了 Hugo,核心原因有三个。
第一,Hugo 的默认构建速度惊人。我本地放了三百多篇测试文章,Hugo 全量构建只要一秒钟左右,Jekyll 和 Hexo 都会有明显的等待时间。写作频繁的人都知道,预览响应越快,改稿就越有耐心。
第二,Hugo 的安装和运行极度干净。它就是一个可执行文件,不需要折腾 Ruby 版本、不需要维护 node_modules,换电脑之后下载二进制就能跑,这种确定性对于个人项目太重要了。
第三,Hugo 的模板和内容组织方式很符合“内容与样式分离”的理念。它把 Markdown 源文件、模板布局、静态资源各自放在独立目录里,结构简单到可以复用很多年。
2.2 Hugo 目录结构设计与内容组织思路
colibri 的目录结构保持了 Hugo 官方推荐的形态,但我在细节上做了适合自己的调整:
colibri/ ├── archetypes/ # 内容模板,比如 posts.md 定义了 front matter 预设 ├── assets/ # 需要经过 Hugo Pipeline 处理的 CSS、图片 │ ├── css/ │ │ └── main.css │ └── images/ # 原始图片素材 ├── content/ # 所有的 Markdown 文章 │ ├── posts/ │ └── about.md ├── layouts/ # 模板布局 │ ├── _default/ │ │ ├── baseof.html │ │ ├── list.html │ │ └── single.html │ ├── partials/ │ │ ├── head.html │ │ ├── header.html │ │ └── footer.html │ └── index.html ├── static/ # 直接复制到根目录的静态文件 │ ├── favicon.ico │ └── robots.txt └── hugo.toml # 全局配置文件这个结构和官方默认差别不大,真正的区别在内容组织思路上。我没有把站点做成“分类 + 标签 + 系列”三重维度,而是只保留了两个:posts/ 目录下面所有 Markdown 文件都是平铺的文章,标签只是文章头部 metadata 里的一个字段,不生成独立归档页。为什么这么干?因为分类、标签、系列这三个维度很容易互相纠缠,维护成本会指数上升。实际写作中,单篇文章只需要一个日期、一个标题、一个摘要、一个标签列表,平铺式管理最省心。
2.3 为什么没有选择前端框架和 CMS
很多朋友会问我:“既然想要好的交互体验,为什么不直接用 Vue/React?或者直接上一个 Headless CMS 不也能很快?”
我对这个问题的回答是:博客的场景决定技术形态,而不是反过来。博客的绝大多数页面是服务端静态渲染的纯信息页面,交互密度极低,用户进来就是读文章、点链接、返回列表,根本不需要一个维护大型状态管理的前端框架。引入 React 或 Vue 意味着引入 JavaScript 运行时、组件编译、路由管理等一大批复杂度,第一次加载却拿不到任何实际收益。
Headless CMS 我也认真考虑过,它确实能解决“在手机上快速发文章”的需求,但是它会引入二次构建、API 鉴权、数据同步等额外环节。对个人博客来说,最稳定的发布链路反而是 Git 仓库加 Markdown:在任何设备上写完,commit 一下,触发部署,结束。这个流程没有中间态,数据长时间不会丢失,也不依赖第三方服务商是否还在运营。
3. 核心页面设计与写作体验优化:不堆功能,只考虑阅读
3.1 从一篇范文开始定义设计系统
设计 colibri 的时候,我没有先去调颜色、选字体,而是先在 content/posts/ 里写了十篇样章,包含不同长度的标题、多级小标题、代码块、引用、长段落、图片、表格。为什么要先有内容再设计?因为页面结构必须服从内容形态,脱离了真实文本去调样式,出来的都是花架子。样章写完之后,我反复阅读,记录下阅读过程中踩到的每一个不舒服的点,比如标题间距不够、代码块宽度溢出、行高太窄导致长句读起来费劲等等。
基于这些真实阅读体验,我建立了 colibri 的视觉基线:
- 正文字号设定在 17px 到 18px 之间,行高 1.75,段落间距用 margin 控制而不是靠空行。
- 正文最大宽度控制在 68ch 左右,避免在宽屏显示器上形成过长文本行。
- 标题层级只用两级:h2 作为大节标题,h3 作为小节标题,不出现四五六级标题,从结构上倒逼我把文章组织得更清晰。
- 代码块采用等宽字体,背景使用低对比度的灰色,不让代码抢正文的视觉重心。
- 所有颜色都经过 WCAG AA 对比度检查,深色模式单独调试,避免切换后出现字体看不清的问题。
这个设计没有追求“惊艳”,要的就是“耐看”。博客的核心是文字,花哨的设计只会增加认知负担。
3.2 封面、列表与文章页的布局逻辑
colibri 的首页列表摒弃了常见的“标题 + 日期”一行式列表,改用卡片式布局,但每张卡片都很克制:包含文章标题、发布日期、一行摘要、标签。卡片由 CSS Grid 控制列数,在移动端自动退化为单列。这个卡片并不复杂,但解决了“信息密度”问题,读者扫一眼就能判断哪些文章值得点进去。
文章页的布局只有一个内容栏,侧边栏彻底砍掉了。很多博客喜欢在侧边栏放热门文章、标签云、作者介绍,看起来内容丰富,实际上只会分散注意力。我的文章页从上到下依次是:文章标题、发布日期、正文、底部导航(上一篇/下一篇)、一个极简版权声明。没有“猜你喜欢”,没有“最新文章”,没有弹窗订阅。
这样的布局在第一次上线时确实显得有点“空”,但使用一段时间后我越来越确信,这种空恰恰是种保护。读者进来就是看内容,看完就走,干脆利落。
3.3 写作体验的隐性优化:Front Matter 与模板预设
除了页面样式,写作体验也是 colibri 的重要优化对象。我用 Hugo 的 archetypes 机制给每一篇新文章预设好了 Front Matter 模板,新增文章时只需要运行hugo new posts/xxx.md,文件里就会自动生成:
--- title: "文章标题" slug: "" date: 2025-01-15T10:00:00+08:00 description: "一句话描述,会显示在列表和搜索引擎结果里" tags: [] draft: true ---这样写文章的时候,我只需要关心正文内容,不用每次重复填写 metadata。尤其是description字段,很多博客搭建者会忽略它,导致社交媒体分享卡片和搜索结果里显示出很丑的默认摘要。colibri 在列表页和<head>里都读取了这个字段,写文章时多花十秒钟填一句描述,能显著提升页面的转发效果。
另外,我在模板里针对代码块做了一层额外的处理。Hugo 自带的高亮功能会把代码包装成很长的 HTML 结构,我通过配置选择了基于 Chroma 的纯 CSS 高亮模式,不生成任何 JavaScript,这样代码块好看但不拖慢页面。
4. 性能实测与移动端体验调优:数据不会说谎
4.1 用 Lighthouse 和 WebPageTest 建立性能基线
colibri 上线后的第一件事,不是庆祝,而是跑一轮性能测试。我用 Lighthouse 在桌面端和移动端分别测试,同时也用 WebPageTest 做了多地区测试,拿到了一组基线数据:
| 指标 | 桌面端 | 移动端(模拟慢速网络) |
|---|---|---|
| 首次内容绘制(FCP) | 0.4s | 0.9s |
| 最大内容绘制(LCP) | 0.8s | 1.6s |
| 累积布局偏移(CLS) | 0.001 | 0.001 |
| 总阻塞时间(TBT) | 0ms | 10ms |
| 性能分数 | 99 | 98 |
这个成绩不算顶尖,但对于一个纯静态博客来说已经非常可观。能有这个结果,主要不是因为我做了什么惊天动地的优化,而是因为我一开始就选择了一条性能上限足够高的技术路线,后面只需要把细节擦干净就行。
我总结下来的三条核心性能策略:第一,不加载网络字体,直接使用 system-ui 字体栈,省掉字体文件下载和 swap 带来的文字跳动;第二,所有图片通过 Hugo 的图片处理功能压缩成 WebP 格式,并按需生成多个尺寸,移动端不会下载桌面端的大图;第三,CSS 文件不分割,全部合并压缩成单个文件,内联在 HTML 的<head>里,避免额外的网络请求和渲染阻塞。
4.2 移动端真正需要的优化点
移动端优化有个容易忽略的误区:不是光靠“响应式布局”就能解决所有问题,真正的差别在资源加载策略。我拿手机访问 colibri 时做过一次实际的网络抓包,发现问题主要集中在图片加载。原来自带的图片是 2500px 宽的 JPEG,单张就好几百 KB,在 Wi-Fi 下感觉不明显,但切到 4G 网络就原形毕露。
后面我通过 Hugo 的 image resource 处理,把每张文章的封面图按照 480px、768px、1280px 三档尺寸输出,并且在模板中用srcset配合sizes属性控制浏览器按需选择:
<img srcset="/img/post-480.webp 480w, /img/post-768.webp 768w, /img/post-1280.webp 1280w" sizes="(max-width: 600px) 100vw, 768px" src="/img/post-768.webp" alt="文章封面图" width="768" height="432" loading="lazy" decoding="async" />这里有个容易被忽略的细节:图片的width和height属性一定要写,不要省略。很多人会发现页面文字加载完的瞬间图片区域是空白的,等图片加载完文字又猛地被顶下去,这就是 CLS 的典型来源。只要 HTML 里声明了宽高比例,浏览器在图片加载前就能预留对应空间,布局偏移直接归零。
移动端的第二根硬骨头是点击区域的尺寸。设计师在桌面浏览器上觉得“这个链接 14px 就够了”,但到了手机上,人类手指的精度远没有鼠标高。colibri 的导航链接、标签链接、分页按钮,最小触摸区域都保证在 44px 以上。这个不是风格偏好,是触屏设备的可用性底线。
4.3 实测之后的一次“负优化”回滚
优化过程中不是所有尝试都值得保留,我也踩过回滚的坑。有一版 colibri 为了实现首页的阅读进度条,引入了一个两百行的 JavaScript 插件。进度条确实很酷,用户往下滑动时顶部会有彩色条显示阅读百分比,但我在移动端测试后发现,这个插件导致主线程的长时间任务增加,TBT 从 0ms 涨到了 85ms,而且滑动的流畅度有明显下降。
我在体验和性能之间犹豫了两天,最后决定把进度条去掉。反思之后我意识到,这类装饰性功能带来的体验增益很难量化,但性能上的损失是实打实的。一个博客的价值在于文字内容,而不是用户能精确知道“我已经读了百分之几”,这个功能值得被砍掉。
类似被砍掉的功能还有图片灯箱、返回顶部按钮、分享到社交媒体的悬浮按钮。每个功能在单独看的时候都“很有必要”,放到页面上才发现它们共同组成了一堆噪音。colibri 最终留在页面上的 JavaScript 只有一个:用于实现移动端导航菜单展开的十行以内的小脚本,其余全部是零脚本渲染。
5. 踩过的坑与后续扩展方向:给同样在做极简博客的人提个醒
5.1 Hugo 升级带来的兼容性教训
个人项目最容易忽略的就是依赖升级的兼容性问题。colibri 用的是 Hugo,它本身是一个持续迭代的工具,小版本升级通常是平滑的,但跨大版本升级时很容易出问题。我在一次从 0.13x 升级到 0.14x 的时候,站点构建直接报错,排查了半天发现是新版本对某些模板函数的执行上下文做了更严格的校验,我自定义的一个 shortcode 写法不再被支持。
这事的直接损失不大,但它让我建立了一个习惯:每次升级前,先看 release notes,并且把升级前的版本号写进一个 CHANGELOG 文件里;升级后用hugo --gc清理缓存并全量构建一次,用 diff 对比生成的 HTML 是否出现异常变化。博客虽然是个小项目,但凡是涉及线上更新的事,都要有可回滚的预案。
5.2 图片处理:Hugo Pipeline 的边界与静态素材的取舍
Hugo 的图片处理能力很强,但它不是万能的。我在 colibri 中把放在 assets 目录里的图片通过resources.Get加载并调用.Process方法处理成多种格式,这个流程在构建时可以把图片压缩得很好。但它的代价是构建时间变长,尤其是图片很多或者原图很大的时候,每次构建都要重新处理一张图,累积的耗时很可观。
对于偶尔才写一篇配图文章的人来说,这个效率问题并不严重。但如果你计划高频更新,我建议每次都先把原图压到 1600px 宽以内再放进 content 里,不要依赖构建工具去处理 5000px 的超大原图。既节省构建时间,也避免构建爆内存。Hugo 在图片处理时确实是先加载到内存再处理的,超大图片在 CI 环境里可能直接导致构建失败。
5.3 搜索、评论、多语言:哪些功能可以晚点再要
colibri 上线初期没有搜索功能,开始我觉得没关系,但文章数量超过一百篇后,确实有读者反馈找历史文章不方便。我没有立刻引入外部搜索服务,而是考虑了两套轻量方案:
- 第一套方案:用 Hugo 预生成一个包含所有文章标题和摘要的 JSON 文件,然后在前端放一个搜索框,用 JavaScript 在本地执行简单的关键词匹配。这个方案适合中小型博客,零服务器成本,搜索速度也很快。
- 第二套方案:使用第三方搜索服务,比如 Algolia 或者 Pagefind。Pagefind 是一个静态站点的离线搜索方案,构建时生成索引,搜索过程全部在前端完成,不需要后端。它的精度比自写 JSON 匹配高,配置也不复杂。
我最后选择了 Pagefind,因为它解决了一个自写方案很难解决的问题——中文分词。中文不像英文按空格分词,自写的“包含判断”很容易出现奇怪的匹配结果,Pagefind 内置了针对中文的分词支持,体验明显更好。
评论系统我到现在都没有接入。评论区本质上是一个需要后端、需要内容审核、需要反垃圾的复杂模块,个人博客硬上评论功能会给自己找很多麻烦。如果哪天真的需要互动,我更倾向于在文章底部放一个“欢迎邮件交流”的链接,让沟通回归异步文字,而不是在页面上挂一个需要维护的实时评论区。
5.4 下一步:内容才是 colibri 真正的主角
现在 colibri 的代码和主题已经稳定下来,我不会再往里面加大的功能模块了。后续的演进方向反而是内容层面的:持续给 tag 建立更稳定的命名体系,让归档可读性更高;把更多旧笔记迁移到 colibri 的目录结构里,形成统一的知识库;条件成熟时再增加一个简单的英文版本,同样以静态页面方式输出,不引入国际化框架。
一个博客项目能走多远,很大程度上取决于它的技术门槛有多低、维护成本有多低。colibri 这个名字对我来说是一种提醒:做一个像蜂鸟一样的站点,身体轻巧,反应迅速,悬停在文字之上,不打扰读者,不拖累创作者。如果你正在规划自己的博客,希望这篇记录能给你一些启发——先想清楚要砍掉什么,再考虑要增加什么,做一个轻巧、敏捷、精准停留的小站,往往比做一个“什么都有”的庞然大物更值得。