我的博客架构长什么样

很多人好奇这个博客是怎么搭的.

看起来很简单,但背后东西不少.算不上大工程,但从工程角度确实折腾了不少东西.双语架构,自动化构建, PWA, Google SEO 等等.

所以打算写个系列,把现在的这套东西记录下来.用什么,为什么这么选,代码怎么写的,性能上怎么考虑的.

整体架构

Markdown 内容 (中英双语)
      ↓
  Hugo SSG (extended, v0.164)
      ↓
  自定义主题 (fork cactus-dark, 大改)
      ↓
  Gulp Pipeline
    ├── hugo --minify
    └── Workbox generateSW
      ↓
  Cloudflare Pages (GitHub Actions CI)

三层: 内容层, 构建层, 部署层. 没有数据库, 没有后端, 全是静态文件.

为什么用 Hugo

我是前端, 喜欢用前端圈的东西. Hugo 是用 Go 写的, 文章多了以后构建速度就是快. 两百多篇文章, 几秒就构建完了. 这就是核心原因.

构建快了, 更新博客的心理负担就小. 这是一个正反馈.

Go 模板用起来也挺顺手, 变量作用域, 管道语法, partial 系统, 该有的都有. 不过刚开始接触的时候, {{ range }}{{ with }} 的区别可能会绕一下.

为什么用 Gulp

Hugo 只管静态生成. 但我还需要集成 service worker 的生成和一些后处理. Gulp 就是用来干这个的. 不需要复杂的构建系统, 几个任务串起来就行.

整个 gulpfile.js 大概五十行:

const gulp = require("gulp");
const shell = require('gulp-shell');
const workbox = require('workbox-build');

gulp.task('build', () => gulp.src('./').pipe(shell(['hugo --buildFuture --minify'])));

gulp.task('generate-service-worker', () => {
    return workbox.generateSW({
        globDirectory: './public',
        globPatterns: ['**/*.{woff2,woff,js,css,png.jpg}'],
        globIgnores: ['sw.js'],
        swDest: `./public/sw.js`,
        clientsClaim: true,
        skipWaiting: true,
        runtimeCaching: [
            { urlPattern: /.*\.js/,    handler: 'NetworkFirst' },
            { urlPattern: /.*\.css/,   handler: 'StaleWhileRevalidate' },
            { urlPattern: /.*\.(?:png|jpg|jpeg|svg|gif)/, handler: 'CacheFirst',
              options: { cacheName: 'images', expiration: { maxEntries: 50 } } },
            { urlPattern: /.*\.html/,  handler: 'NetworkFirst' }
        ]
    })
    .then(() => console.info('SW 生成完成'))
    .catch(error => console.warn('SW 生成失败: ' + error));
});

gulp.task("default", gulp.series('build', 'generate-service-worker'));

两个任务, 串行执行. 先构建, 再生成 SW.

为什么用 Cloudflare Pages

静态站点零配置, 全球 CDN, 免费额度够用. GitHub Actions 集成很简单, push 到 master 自动构建部署. 唯一要提一下: 第一次部署的时候, Cloudflare Pages 的项目名不能随便改, 改了之后域名映射要重新配, 有点蛋疼.

- name: Deploy to Cloudflare Pages
  run: npx wrangler pages deploy public --project-name=time-friend

CI 文件一共 35 行. 装 Hugo, 装依赖, 构建, 部署.

性能优化

构建速度

Hugo 的构建速度跟文章数量关系不大, 始终是秒级. --minify 会把 HTML 层面的压缩做掉, 不需要额外处理.

页面加载

纯静态 HTML, 没有服务端渲染开销. 主题 CSS 大概 20KB, 加载方式很直接. 默认没有阻塞渲染的 JS, 脚本都在页面底部的 js.html partial 里加载.

缓存策略

Service Worker 对不同资源做了不同的缓存策略. 具体每种策略为什么这么选, 单独写了一篇: 如何让博客支持离线访问.

第一次访问后, 大部分资源都从缓存走了, 后面的导航几乎是秒开.

Google SEO

每篇文章在构建时自动注入 JSON-LD Article 结构化数据. 在 head.html 里:

<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": "{{ .Title }}",
    "datePublished": "{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}",
    "dateModified": "{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}",
    "author": { "@type": "Person", "name": "{{ .Site.Params.author }}" },
    "description": "{{ .Description }}",
    "url": "{{ .Permalink }}"
    {{ with .Params.tags }}
    ,"keywords": "{{ delimit . ", " }}"
    {{ end }}
}
</script>

Google Search Console 通过 DNS 记录验证. Sitemap 由 Hugo 自动生成. 每页有 canonical URL. 就这些, 其他搜索引擎不搞.

系列文章

全部写在一起了, 没有固定顺序, 方便随时加:

  • 概览 (这篇) — 架构概览, 技术选型, 为什么这么选
  • 首页打字效果 — Typed.js, quotes.json, 实现逻辑
  • i18n 国际化 — 内容国际化 + 界面国际化, translationKey, module mounts
  • 主题定制 — 覆盖了哪些模板, 加了什么功能
  • 构建流水线 — Gulp 任务详解, CI/CD
  • Google SEO — 结构化数据, meta 标签, sitemap, 验证
  • 部署 — Cloudflare Pages 配置, 环境管理, 域名
  • 评论系统 — Giscus 对接, GitHub Discussions, 完整流程
  • 页面统计 — Busuanzi + Google Analytics 两套方案
  • Service Worker — 什么是 SW, 缓存策略, 注册流程

文章链接:

/zh/archive/blog-architecture-overview/

# 相关文章推荐