为什么自己写主题?

市面上不缺 Hugo 主题。但当我一个个试用过去,发现它们总是在某个地方不符合期待——要么配色太冷淡,要么动画太浮夸,要么配置太复杂。

与其在几十个主题中寻找"差不多"的那个,不如花一个周末自己写。结果一个周末变成了三个月。

这篇文章记录了这个过程中的关键决策和技术要点。

项目结构:Less is More

Hugo 主题的最小结构其实很简单:

themes/illusion/
├── layouts/          # 模板(必须)
│   ├── _default/
│   │   └── baseof.html   # 外壳模板
│   └── index.html        # 首页模板
├── static/           # 静态文件
└── theme.toml        # 主题描述文件

但在开发中逐渐扩展为完整架构:

themes/illusion/
├── assets/
│   ├── scss/main.scss    # 样式 → Hugo Dart Sass 编译
│   └── ts/main.ts        # 脚本 → Hugo js.Build 编译
├── data/theme.yaml       # 数据文件 → Hugo 自动挂载
├── i18n/zh-CN.yaml       # 翻译 → Hugo i18n 系统
└── archetypes/default.md # 文章模板

没有 package.json,没有 node_modules。所有的构建都通过 Hugo 内置管道完成。

模板系统:组件化思考

Hugo 使用 Go 的 html/template 引擎。初看语法很奇怪——{{ .Title }}{{ range .Pages }}{{ partial "foo" . }}——但习惯后会发现它强制了一种良好的架构:模板必须组件化。

baseof.html:一切的外壳

baseof.html 是 Hugo 的布局基础,所有页面都包裹在其中:

<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
<head>
  {{ partial "_layout/head.html" . }}
</head>
<body>
  {{ partial "_layout/header.html" . }}
  <main>
    {{ block "main" . }}{{ end }}
  </main>
  {{ partial "_layout/footer.html" . }}
</body>
</html>

{{ block "main" . }} 是插槽,由具体页面模板填充。这相当于一个简单的模板继承机制。

Partial 分层

幻梦的 partials 分为四个目录:

目录职责示例
_layout/全局布局head, header, footer, menu
_components/可复用 UIarticle-card, page-hero, sidebar
_shortcodes/短代码skills, link-card
_utils/工具函数relative-time

这种分层让模板结构清晰。修改导航不需要翻开文章卡片的代码,反之亦然。

数据驱动:一个 YAML 管理全站

幻梦最大的设计决策之一是数据文件集中化。所有页面内容全部在 data/theme.yaml 中定义。

# data/theme.yaml
home:
  hero:
    title: "爱则心痛"
    subtitles: ["技术开发者", "代码艺术家"]
about:
  intro:
    name: "爱则心痛 (azxt)"
    quote: "代码如诗,技术如画。"

模板中通过 hugo.Data.theme 访问,站点根目录同名文件自动覆盖主题默认值。

资源管道:CSS 和 JS 的现代化

SCSS → CSS

{{ $css := resources.Get "scss/main.scss" | toCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">

toCSS 使用 Hugo 内置的 Dart Sass(需要 extended 版本)。

TypeScript → JavaScript

{{ $js := resources.Get "ts/main.ts" | js.Build (dict "target" "es2015" "format" "iife") | minify }}
<script src="{{ $js.RelPermalink }}" defer></script>

Hugo 的 js.Build 使用 esbuild,编译速度极快(通常 < 50ms)。

第三方库:CDN 为主,本地兜底

幻梦依赖三个库:AOS.js、Typed.js、Font Awesome 6。加载策略是"CDN 优先 + 本地兜底"——CDN 加载失败时自动回退到 static/lib/ 下的本地副本。

开发工作流

hugo server --disableFastRender   # 开发
hugo --minify --cleanDestinationDir  # 构建
hugo server -D                    # 预览草稿

hugo server 自带 LiveReload,加上毫秒级的构建速度,开发体验非常流畅。


写主题最深的体会是:好的框架让你专注于设计本身,而不是配置工具链。Hugo 在这方面做到了极致。