本文是 Hugo + PaperMod 博客搭建:从 0 到评论上线,我踩过的坑 的「完整版」——上篇是叙事版,这篇是参考手册版。读这一篇就能掌握搭建这个博客用到的全部 Hugo 知识。
1. 什么是 Hugo
Hugo 是一个用 Go 写的静态站点生成器(Static Site Generator, SSG)。你写 Markdown,Hugo 编译成 HTML,浏览器直接读 HTML 文件。
跟同类对比:
| 工具 | 语言 | 特点 |
|---|---|---|
| Hugo | Go | 快——几千页也毫秒级编译 |
| Jekyll | Ruby | 老牌,GitHub Pages 原生支持 |
| Hexo | Node.js | 中文社区多 |
| Gatsby | React + GraphQL | 适合复杂前端 |
| Astro | 多框架 | 现代,新兴 |
Hugo 的最大卖点是速度——几千篇文章的站,一次完整 build 通常 < 1 秒。本地 hugo server 改一行就热更新,几乎无感。
Info> Hugo 是「无依赖」工具——单文件可执行(~30 MB),不装 Node、不装 Ruby、不装数据库。丢任何机器上都能跑。 >
2. 安装
macOS
| |
Linux
| |
Warning> 装 **`extended`** 版本——后面要用 Hugo Pipes 处理 SCSS,普通版不支持。 >
Windows
| |
验证
| |
3. 初始化一个新站
| |
生成结构:
| |
4. 装主题
| |
在 hugo.yaml 里启用:
| |
themes/ 是子模块,要随仓库一起 clone 时用:
| |
5. 第一个 post
| |
archetypes/default.md 是模板(这个博客里我们的 archetype 注释了所有可用字段)。生成的 post 长这样:
| |
draft: true 表示草稿——本地预览能看到,hugo 构建时会被忽略。发布前改 false 或删掉。
6. 启动 dev server
| |
-D或--buildDrafts:显示 draft 文章- 默认监听
http://localhost:1313 - 改文件自动热更新——浏览器刷新就能看到
7. 构建生产版本
| |
输出到 public/ 目录。整个目录就是静态站点,丢任何静态托管就行(GitHub Pages、Vercel、Netlify、Nginx 都可)。
8. Markdown 速览
Hugo 用 Goldmark 解析 Markdown,支持 GFM(GitHub Flavored Markdown)扩展:
| |
代码块支持语法高亮(Hugo 内置 Chroma),指定语言就行:
| |
9. Front matter 完全指南
Front matter 是 post 顶部的 YAML(或 TOML/JSON),给 Hugo 机器读。可用的字段远不止你看到的那些:
| |
日期的时区
+08:00 是东八区时间。Hugo 默认按 UTC 存,时区错了可能导致「未来时间」post 不显示。要么删掉时区,要么写对时区。
10. 配置文件 hugo.yaml
从 Hugo 0.110 开始推荐用 YAML(之前是 TOML)。基本结构:
| |
Info> 想知道某个字段是干啥的?三个办法: > 1. 读主题的 `hugo.yaml` 或 `exampleSite/`——主题作者会示范 > 2. 读主题的 `layouts/_partials/`——看哪些字段被消费了 > 3. Hugo 文档:https://gohugo.io/getting-started/ >
11. 内容组织
Sections
content/posts/ 就是一个 section——Hugo 自动把它当列表页(/posts/)。默认行为:
content/posts/foo.md→ 渲染成/posts/foo/(单页)content/posts/_index.md→ 渲染成/posts/(列表页)content/posts/本身 → 列表页
Page Bundles
Hugo 推荐「Page Bundle」——把一篇 post 的所有资源(图片、附件)放在同名文件夹里:
| |
这样 index.md 里写 ,Hugo 自动处理路径。搬文章时不会丢图。
Archetypes
每类内容的「默认 front matter 模板」。比如想让所有 post 默认 draft: true:
archetypes/posts.md:
| |
然后 hugo new posts/foo.md 会自动套这个模板。
Info> 我们这个博客的 `archetypes/default.md` 注释了所有常用字段,新文章创建时一眼能看到能用什么。 >
12. Taxonomies
Taxonomy = 分类法。Hugo 内置支持任意多套分类,常见的有:
- tags(标签):一篇文章可以多个,扁平
- categories(分类):一篇文章通常一个,层级
- 任意自定义:比如「系列」「作者」「难度」
启用方式(在 hugo.yaml):
| |
post front matter 写:
| |
Hugo 自动生成:
/tags/—— 所有标签/tags/hugo/—— 该标签下的所有 post/categories/技术/—— 该分类下的所有 post/series/博客搭建/—— 该系列的所有 post
PaperMod 内置渲染这些列表页,不用写模板。
排序:weight
列表页默认按 date 倒序。想手动控制顺序:
| |
混合用法(有的有 weight 有的没有)会出现奇怪的排序——无 weight 的全归 0,会挤到最前。要么全设 weight,要么全不设。
13. Shortcodes
Shortcode = Hugo 的「组件」机制,让 Markdown 调用预定义的 HTML 模板。
内置 shortcode
| |
自定义 shortcode
放在 layouts/_shortcodes/<name>.html。
举例——一个简单的「笔记」框:
layouts/_shortcodes/note.html:
| |
用法:
| |
上面是举例。围栏代码块里的 shortcode 语法默认仍会被 Hugo 解析,想原样展示要用
/开头的转义形式(/*和*/包裹),或放在 HTML 注释里。
调用方式:
- 不带内容的 shortcode:尖括号百分号 + name + 空格 + 任意参数 + 百分号尖括号
- 带内容的 shortcode:同上开始,中间写 HTML/Markdown,结尾用同名结束标签加
/
Hugo 处理 fenced 代码块里的
{{<和反引号里的{{<都照样当 shortcode 解析。文档里要展示 shortcode 语法本身,得用/前后包裹(开头{{< /* name */ >}}形式),或者用 HTML 注释包起来。
实战:我们的 alert / card / video
我们博客里写了三个 shortcode,都在 layouts/_shortcodes/:
| Shortcode | 用途 | 是否需要 inner |
|---|---|---|
alert | 4 种 variant 的提示框(info/success/warning/danger) | 需要 |
card | 带图片的链接卡片 | 自闭合 |
video | 嵌入 YouTube / Bilibili / 自托管视频 | 自闭合 |
video 的核心是根据 URL 判断平台并出对应 iframe:
| |
14. 模板系统(Go templates)
Hugo 用 Go template 写 HTML。这是 Hugo 最难也最有威力的一块——能完全控制输出。
基础语法
| |
.(点)是当前 context。partial "x" . 的第二个参数 . 是传给 partial 的 context。
Hugo 对象导航
最重要的几个 .Site、Page、Permalink:
| |
内置函数(高频)
| 函数 | 用途 |
|---|---|
len, first, last, slice | 集合操作 |
sort, where, group | 排序、过滤、分组 |
findRE, strings.Contains | 正则、字符串包含 |
printf, safeHTML | 格式化、标记为安全 HTML |
time.Format | 时间格式化 |
default | 提供默认值 |
partial, template | 引入子模板 |
dict | 构造 map(传多参数给 partial) |
Warning> Hugo 的函数链是**管道**风格:`$result | func1 | func2`。中间结果是第一个参数。 >
写一个自定义 list 模板
layouts/_default/list.html(默认列表页):
| |
15. 覆盖主题(Override)
这是 Hugo 最 powerful 的特性——不用 fork 主题就能改任何东西。
主题文件在 themes/PaperMod/layouts/...。Hugo 查找顺序:项目根目录的 layouts/ 优先于 themes/。
所以你想改 PaperMod 的某段 HTML:
- 找到那个文件:
themes/PaperMod/layouts/_partials/header.html - 复制到你的项目:
layouts/_partials/header.html - 改它——主题的同路径文件被忽略
实战:我们的 extend_post_content.html
PaperMod 默认提供了 hook:layouts/_partials/extend_post_content.html——如果这个文件存在,会被自动插入到 post 正文之后。
我们在那里挂了:
- 系列文章 box(显示「本文属于系列 X」+ 上下篇)
- 双链 box(显示「引用本文」)
完整逻辑见 layouts/_partials/extend_post_content.html 和 layouts/_partials/backlinks.html。
实战:双链的 Go template
layouts/_partials/backlinks.html 的核心逻辑:
| |
关键 trick:用 .Content(渲染后 HTML)而不是 .RawContent(markdown)来搜链接——因为前者不管你是 {{< ref >}} 还是 /posts/foo/ 还是绝对 URL,都规范化成同一个 href="/posts/foo/"。
16. 多语言(高级)
Hugo 支持多语言站,每种语言一套内容树:
| |
配置:
| |
Hugo 自动按文件名 + slug 配对翻译版,PaperMod 自动加语言切换器。
Warning> 我们试过加英文,但只有一个英文 post 时语言切换器体验很糟(找不到对应翻译就跳英文首页)。**至少要 5+ 篇英文才有意义开双语。** >
17. 部署
GitHub Pages + Actions
.github/workflows/hugo.yaml:
| |
我们的部署比这还简化——直接 actions/checkout + actions/setup-hugo + actions/upload-pages-artifact + actions/deploy-pages,是 GitHub 官方 Pages 部署流程。
静态托管的其他选项
| 平台 | 适合 |
|---|---|
| GitHub Pages | 开源项目、个人站,免费 |
| Vercel | 配合 SSG + Serverless Functions |
| Netlify | 表单、Functions、Identity |
| Cloudflare Pages | 全球 CDN,免费额度大 |
18. 性能调优
构建期
| |
配置期
| |
图片
| |
assets/img/foo.jpg 在 markdown 里用 {{< imgproc foo Fill "800x400" >}} 可以自动裁剪。
缓存
| |
19. 调试技巧
看 Hugo 在 build 时干啥
| |
看 page 有什么变量
| |
或者只 dump 一个:
| |
常见坑
| 坑 | 解决 |
|---|---|
| post 不显示 | 检查 draft: false + date 不是未来 |
| 日期乱序 | date 必须能 parse;混用 ISO 字符串和 time.Time 会出问题 |
| 改主题没生效 | 检查是不是放在 themes/ 而非项目根目录的 layouts/ |
| 分类页 404 | hugo.yaml 加 taxonomies: 配置 |
| 主题版本太老 | PaperMod ≥ 某版本才支持某些 feature |
20. 资源
- 官方文档:https://gohugo.io/documentation/
- PaperMod 文档:https://github.com/adityatelange/hugo-PaperMod/wiki
- Hugo 论坛:https://discourse.gohugo.io/
- 我们的博客源码:每篇文章的 front matter 和模板都开源在 GitHub
小结
Hugo 的核心模型其实很简洁:
- Markdown 文件 → 内容
- hugo.yaml → 配置
- 主题 → 默认模板
- layouts/ → 自定义覆盖
hugo命令 → 一行编译
学会这五件事,剩下都是细节。遇到问题先想:是配置不对?是主题问题?还是我的 override 没生效?——这三类各查各的,效率最高。
下一篇打算写「GitHub Actions 完整指南——从零配置到部署」——配合这个博客的部署流程讲。有想看的具体话题告诉我。