从使用者角度整理的 Hugo 常识,够用就行。
目录结构
blog/
├── hugo.toml # 主配置文件(也可用 config.toml/yaml)
├── content/ # 所有内容(文章、页面)
│ ├── posts/ # 博客文章
│ └── about.md # 单页(/about/)
├── layouts/ # 自定义模板(覆盖主题)
│ ├── _default/
│ │ ├── baseof.html # 基础模板
│ │ ├── single.html # 单页模板
│ │ └── list.html # 列表页模板
│ └── partials/ # 可复用片段
├── themes/ # 主题目录
├── assets/ # 需要处理的资源
│ └── css/extended/ # 自定义 CSS(自动合并)
├── static/ # 静态文件(直接复制)
└── public/ # 构建输出(勿手动编辑)
构建原理
理解 Hugo 的构建流程,关键是看懂各目录的角色:
┌─────────────────────────────────────────────────────────────────┐
│ Hugo 构建流程 │
└─────────────────────────────────────────────────────────────────┘
输入层(你写的) 处理层 输出层
────────────── ────────── ──────────
┌──────────────┐
│ hugo.toml │─────────────────────┐
│ (配置) │ │
└──────────────┘ ▼
┌─────────────────┐
┌──────────────┐ │ │
│ content/ │───────────▶│ │
│ (Markdown) │ │ Hugo │
└──────────────┘ │ 引擎 │
│ │
┌──────────────┐ │ 模板 + 内容 │
│ layouts/ │───────────▶│ + 资源 = HTML │
│ (模板) │ │ │
└──────────────┘ │ │ ┌──────────────┐
│ │───────────────▶│ public/ │
┌──────────────┐ │ │ │ (输出) │
│ assets/ │───────────▶│ │ │ │
│ (资源管道) │ │ │ │ index.html │
└──────────────┘ │ │ │ posts/ │
│ │ │ css/ │
┌──────────────┐ │ │ │ ... │
│ static/ │───────────▶│ │ │ │
│ (直接复制) │ └─────────────────┘ └──────────────┘
└──────────────┘
各目录的角色
| 目录 | 角色 | 构建时行为 |
|---|---|---|
content/ |
数据源 | Markdown → 解析为 Page 对象 |
layouts/ |
渲染模板 | Go 模板引擎处理,绑定内容 |
themes/ |
备用模板 | 被 layouts/ 覆盖 |
assets/ |
资源管道 | 处理、压缩、指纹化 |
static/ |
静态资源 | 直接复制到 public/ |
public/ |
输出目录 | 最终静态站点 |
单篇文章的渲染链路
content/posts/my-post.md
│
▼
┌───────────────────────────────────────┐
│ 1. 解析 Front Matter(title, date...) │
│ 2. Markdown → HTML │
│ 3. 确定 Content Type → 选择模板 │
└───────────────────────────────────────┘
│
▼
layouts/_default/single.html 或 themes/xxx/layouts/_default/single.html
│
▼
┌───────────────────────────────────────┐
│ 模板渲染: │
│ - {{ .Title }} → 文章标题 │
│ - {{ .Content }} → 正文 HTML │
│ - {{ partial "toc.html" . }} → 目录 │
└───────────────────────────────────────┘
│
▼
public/posts/my-post/index.html
模板选择逻辑
Hugo 按以下顺序查找模板(优先级从高到低):
1. layouts/posts/single.html ← 针对 posts section
2. layouts/_default/single.html ← 默认单页模板
3. themes/xxx/layouts/posts/single.html
4. themes/xxx/layouts/_default/single.html
找到第一个匹配的模板后停止搜索。这就是为什么在 layouts/ 放同名文件可以覆盖主题模板。
资源管道流程
assets/css/extended/a.css
assets/css/extended/b.css
│
▼ resources.Match "css/extended/*.css"
│
┌─────────────────┐
│ resources.Concat│ → 合并为一个文件
└─────────────────┘
│
▼
┌─────────────────┐
│ resources.Minify│ → 压缩(删除空格注释)
└─────────────────┘
│
▼
┌─────────────────┐
│ fingerprint │ → 生成哈希文件名(缓存控制)
└─────────────────┘
│
▼
public/assets/css/stylesheet.abc123.css
模板优先级
layouts/ ← 最高优先级(你的自定义)
themes/PaperMod/layouts/ ← 主题默认模板
同名文件,layouts/ 覆盖 themes/。部分模板可单独覆盖,比如 layouts/partials/toc.html 只覆盖目录组件。
常用命令
hugo # 构建到 public/
hugo server # 本地预览(默认 localhost:1313)
hugo server -D # 包含草稿
hugo new posts/xxx.md # 新建文章
文章格式
---
title: "文章标题"
date: 2024-01-01
draft: false # true = 草稿,不发布
tags: ["hugo"]
showToc: true # 显示目录
---
正文内容...
样式覆盖方式
| 方式 | 说明 |
|---|---|
assets/css/extended/*.css |
自动合并到主样式表 |
layouts/partials/extend_head.html |
插入额外 <head> 内容 |
static/ |
直接复制,用绝对路径引用 |
CSS 自动合并示例
PaperMod 主题在 head.html 中有这行:
{{- $extended := (resources.Match "css/extended/*.css") | resources.Concat "assets/css/extended.css" | resources.Minify }}
只要把 .css 放进 assets/css/extended/,Hugo 构建时自动加载,无需手动添加 <link> 标签。
配置要点
baseURL = 'https://example.com/'
theme = 'PaperMod'
[params]
ShowToc = true # 显示目录
defaultTheme = "auto" # auto/light/dark
[markup.goldmark.renderer]
unsafe = true # 允许 HTML 标签
关键概念
| 概念 | 说明 |
|---|---|
| Section | content 下的目录,如 posts/ |
| Page Bundle | index.md + 同目录资源(页面级资源管理) |
| Shortcode | 内容中的 {{< xxx >}},扩展 Markdown |
| Partial | 可复用模板片段,{{ partial "xxx.html" . }} |
| Archetype | hugo new 时的默认模板 |
资源管道
Hugo 可以对资源进行处理链:
{{- $css := (resources.Match "css/extended/*.css")
| resources.Concat "assets/css/extended.css"
| resources.Minify }}
处理流程:匹配文件 → 合并 → 压缩 → 输出
就这些,基本够用了。