博文概览 #

这篇文章写给两种人:
- 想在 macOS 上把 Hugo 搭起来的人
- 已经搭起来了,但看着自己的站点,总觉得还差点“网感”的人
我会从「macOS 安装 Hugo」讲到「推到 Git 私有仓库」再到「Vercel 部署上线」,最后把我到目前为止做过的配置、功能、样式改动一口气盘出来。没有玄学,没有“以后也许用得上”的过度设计,只有能跑、好改、可复制。
0. 目标与路线图 #
目标就三个:
- 本地能跑:
hugo server一开,页面正常渲染 - 版本可控:Git 管起来,别把构建产物塞进仓库恶心未来的自己
- 上线省心:Vercel 自动构建 + 自动部署
路线图:
- macOS 安装 Hugo
- 初始化站点 + 选主题(Blowfish)
- 配置与内容结构落地
- 推送到 Git 私有仓库
- Vercel 部署
- 后续:功能和外观的“人模人样化”
1. macOS 安装 Hugo:别搞复杂 #
我用 Homebrew,原因很朴素:少折腾。
brew install hugo
hugo version能输出版本号就行。别在这一步研究“最佳安装姿势”,你要的是博客,不是安装教程写作大赛。
2. 初始化站点:先跑起来再说美 #
在一个空目录里:
hugo new site blog
cd blog然后先确认最小闭环:
hugo server如果这里就报错,先别继续加戏,先把基础的配置问题解决。
3. 主题 Blowfish:选个能用的,别自虐 #
我选择 Blowfish,原因也很现实:成品度高,配置项够用,视觉不丑。
主题接入完成后,最关键的一步是:确保 Hugo 知道你在用这个主题。
我这里的站点配置在 config/_default/hugo.toml,核心就是:
theme = "blowfish"这行如果没生效,你会得到经典名场面:Page not Found,并且日志疯狂提示找不到 layout。不是你的页面不见了,是 Hugo 根本没法渲染。
4. 配置落地:中文、菜单、列表、背景 #
4.1 默认中文 #
我把默认语言切到 zh-cn,配置文件在:
config/_default/hugo.tomlconfig/_default/languages.zh-cn.toml
你可以理解为:站点“默认说中文”,而不是“在中文里夹英文配置”。
4.2 菜单汉化 #
菜单在:
config/_default/menus.zh-cn.toml
常用的就是文章 / 分类 / 标签,把注释解开,改名就完事。别整“菜单生成器”,你不是在写后台管理系统。
4.3 文章列表宫格(卡片视图) #
列表页(比如 /posts/)想要像示例站那样宫格卡片,就改:
config/_default/params.toml的[list]
核心就是开启卡片视图:
showCards = truecardView = true
4.4 首页背景与文章页背景:别把首页配置当文章配置 #
这点我自己踩过坑:你在 [homepage] 里配的 homepageImage 只影响首页,不会自动传到文章页。
文章页要背景(Hero),需要在 [article] 里打开:
showHero = trueheroStyle = "background"
然后背景图来源有两条路:
- 每篇文章单独放
background*/cover*这种命名的图片在文章目录里(page bundle) - 或者全站默认设置
defaultBackgroundImage
一句话:想要背景就显式开 hero,不要指望它“继承”。
5. 内容与目录:我选了“可维护”的结构 #
我现在的文章路径是按日期分层的,比如:
content/posts/2026/01/27/blog/index.md
这种结构的优点:文件系统一眼能看懂,迁移也方便。缺点也很明显:目录层级深,但我宁愿深一点,也不想把所有文章都堆在一个目录里然后靠搜索活着。
6. 评论区:Giscus,省钱但不省心 #
我用 Giscus,基于 GitHub Discussions。优点:免费、干净。缺点:评论的人得有 GitHub 账号。
Blowfish 的机制很直白:你提供一个 partial,它就渲染。
- 评论 partial:
layouts/partials/comments.html - 开关:
config/_default/params.toml的[article] showComments = true
6.1 日/夜模式同步 #
Giscus 默认不会自动跟主题切换同步。我这里的做法是:
- 加载时用当前主题选择
data-theme - 监听
html的darkclass 变化,发postMessage让 giscus 切主题
这不是“炫技”,是因为不做就真的不好用。
7. 阅读时间与阅读数:一个免费,一个看你钱包 #
7.1 阅读时间(X 分钟) #
这是 Hugo 自带的 .ReadingTime。但中文文章如果不启用 CJK 计数,阅读时间会非常离谱或者为 0。
我这里建议在 config/_default/hugo.toml 开:
hasCJKLanguage = true7.2 阅读数(浏览量) #
Blowfish 的“阅读数”是用 Firebase Firestore 统计的:能用,但不是 Hugo 的原生能力。
说人话:你要不想花钱,就别上这个;要上,就把 Firebase 配置、匿名登录和 Firestore 规则弄好。
8. 外观与细节:从“能看”到“像个站” #
8.1 左上角 Logo:日间一张,夜间一张 #
主题默认只支持一个 logo。我不想在夜间模式看“反差刺客”,所以我做了一个小覆盖:
layouts/partials/header/basic.html
支持:
logo = "img/blog-day.png"logoDark = "img/blog-night.png"
图片放 assets/img/,路径用相对路径就行。
8.2 浏览器标签页图标(favicon) #
Hugo 的静态文件覆盖规则很明确:你项目里的 static/ 会覆盖主题的 static/。
但问题是:浏览器不只认 favicon-32x32.png,还认 favicon.ico、site.webmanifest 等一堆东西。你只替换两张 PNG,等于只改了“冰山一角”,剩下的浏览器还是会去拿主题的。
所以我把整套 favicon 放到了 static/,让它们同名覆盖主题资源。
8.3 分类/标签/文章列表页标题中文化(别指望它自动懂中文) #
我一开始在 /categories/ 看到一个大大的 Categories,这不是主题挑衅你,是 Hugo 的默认行为:taxonomy 的标题就是把 categories 首字母大写一下。
想改很简单,给这些页面加 _index.md,用 title 覆盖默认标题:
content/categories/_index.md->title = "分类"content/tags/_index.md->title = "标签"content/posts/_index.md->title = "文章"
注意:description 依然只进 <meta>,页面正文默认不显示(这是主题模板的选择,不是你写错了)。
8.4 分类/标签页也要背景(taxonomy/term 的开关和 list 不是一回事) #
/posts/ 有背景,不代表 /categories/、/tags/ 也会有,因为它们走的是不同的页面类型。
我这里把这两类也显式打开了 hero:
config/_default/params.toml的[taxonomy] showHero = trueconfig/_default/params.toml的[term] showHero = true
同样配 heroStyle = "background",背景图就会跟文章页/列表页用同一套规则(优先页面资源,其次 defaultBackgroundImage)。
9. Git 私有仓库:别把垃圾推上去 #
我加了 .gitignore,至少要忽略这些:
public/(构建产物)resources/(Hugo 缓存).hugo_build.lock
不然你每次构建都能把仓库搞脏,最后你会开始痛恨“曾经的自己”。
10. Vercel 部署:能自动化就别手动 #
思路很简单:
- 代码推到 Git
- Vercel 连接仓库
- 设定构建命令:
hugo --minify - 输出目录:
public
然后你就能享受“我一 push,它就自动上线”的快乐。
11. 我踩过的坑(供你避雷) #
11.1 本地能启动但全站 404 #
根因:主题没启用(theme = "blowfish" 没生效),导致 Hugo 找不到布局模板。
11.2 文章“写了但看不到” #
根因之一:文章时间在未来,且 buildFuture = false。
根因之二:首页不展示 recent(homepage.showRecent = false),所以你以为“文章没了”,其实只是“首页不列出来”。
11.3 右侧目录不显示(别怪主题,先怪自己) #
目录(TOC)出现需要两个条件同时满足:
- 开关打开:
config/_default/params.toml里[article] showTableOfContents = true - 页面真的有“可生成 TOC 的标题”:也就是至少有
##/###这种层级标题
另外 config/_default/markup.toml 里 [tableOfContents] startLevel = 2,意味着 # 一级标题 不会进目录,这是正常的。
11.4 分类/标签“有了但我在目录页没看到文章” #
/categories/、/tags/ 页只列“有哪些分类/标签”,不是文章列表。
你要点进去看:
- 分类:
/categories/教程/ - 标签:
/tags/hugo/(URL 会被自动转成小写/连字符,这是正常的)
如果你刚加完分类标签但页面没刷新,别猜:重启 hugo server,或者直接用 hugo server -D --disableFastRender。
12. 结尾:我还会继续折腾,但不会过度设计 #
博客就是博客,别把它写成“可扩展内容平台”。
想加功能就加,但每次都问自己一句:这玩意儿现在真的需要吗?