这篇将介绍当前博客从 0 折腾到上线的完整记录
先说最终目标#
结果很具体:
- Hugo 本地能正常开发
- Blowfish 主题 + 中文界面
- 文章页有封面、目录、标签
- 接入 Giscus 评论
- 接入 Firebase 阅读量/点赞
- 推到 Vercel 自动部署
如果你的目标和我差不多,这篇基本可以直接复刻
1) 为什么最后选 Hugo#
我一开始在 Hexo、Jekyll、Next.js 之间来回看,最后选 Hugo 的理由就两条:
- 快:本地构建和热更新都很快
- 轻:不依赖 Node.js,装一个二进制就能跑
因为我的开发环境是在MacOS,所以当前文章Mac可以1:1跟着操作,其他系统可以自行尝试
brew install hugo
hugo version
git --versionhugo version 最好确认一下是 extended 版本(很多主题会用到它)。
2) 初始化站点 + 安装 Blowfish#
先创建一个空站点:
hugo new site blog
cd blog接着装 Blowfish。这里我用 git submodule,原因是后续更新主题比较方便,也能清楚知道“主题代码”和“自己代码”的边界。
git init
git submodule add -b main https://github.com/nunocoracao/blowfish.git themes/blowfish我第一次就卡在这里:以为写了 theme = "blowfish" 就能显示,结果页面空白。后来才发现主题目录根本没拉下来 (
装完建议先看一眼目录:
ls themes/blowfish能看到主题文件再继续,不然后面改配置都是白改。
3) 把主题默认配置复制到项目里#
Blowfish 不是只改一个 hugo.toml 就完事。它的大部分配置都在 config/_default/。
这一步必须做:
cp themes/blowfish/config/_default/*.toml config/_default/复制后你会看到这些文件(后面会频繁改):
config/_default/hugo.tomlconfig/_default/languages.en.tomlconfig/_default/menus.en.tomlconfig/_default/markup.tomlconfig/_default/params.toml
4) 中文化(这一步最容易漏)#
Blowfish 的多语言是靠文件名后缀识别的,所以不能只改英文文件内容,而是要复制出中文文件。
cp config/_default/languages.en.toml config/_default/languages.zh-cn.toml
cp config/_default/menus.en.toml config/_default/menus.zh-cn.toml然后改 config/_default/hugo.toml:
theme = "blowfish"
defaultContentLanguage = "zh-cn"
hasCJKLanguage = truehasCJKLanguage = true 这行不要省。中文没有空格,Hugo 默认按英文分词算阅读时间,不开这个会算得很离谱。
4.1 改语言文件#
config/_default/languages.zh-cn.toml 我主要改这些:
languageCode = "zh-cn"
languageName = "中文"
title = "你的博客名"
[params]
displayName = "ZH"
isoCode = "zh-cn"
dateFormat = "2006年1月2日"这里的 2006年1月2日 不是示例日期写着玩的,它是 Go 语言固定的时间模板语法。
4.2 改菜单文件#
config/_default/menus.zh-cn.toml:
[[main]]
name = "文章"
pageRef = "posts"
weight = 10
[[main]]
name = "分类"
pageRef = "categories"
weight = 20
[[main]]
name = "标签"
pageRef = "tags"
weight = 30如果你后面发现导航还是英文,优先检查两件事:
defaultContentLanguage是否真的是zh-cn- 是否真的有
menus.zh-cn.toml(文件名后缀拼错很常见)
5) 把列表页标题也改成中文#
只改菜单不够,列表页标题默认还会显示 Posts。
做法是给每个列表目录加 _index.md:
mkdir -p content/posts content/categories content/tagscontent/posts/_index.md:
---
title: "文章"
---content/categories/_index.md 和 content/tags/_index.md 同理,标题分别写“分类”“标签”。
6) 首页、文章页、列表页参数(都在 params.toml)#
我这部分花的时间最多。建议先别一次改太多,每改一段就开本地服务看效果。
hugo server -D6.1 首页#
config/_default/params.toml:
[homepage]
layout = "background"
homepageImage = "img/background.svg"
showRecent = true
showRecentItems = 5
showMoreLink = true
cardView = true
layoutBackgroundBlur = truelayout 常见有 page/profile/hero/card/background。
我最开始选了 profile,然后纳闷为什么背景图怎么都不出来,后来才知道这个布局本来就不吃 homepageImage。
6.2 文章页#
[article]
showHero = true
heroStyle = "background"
showTableOfContents = true
showTaxonomies = true
showReadingTime = true
showComments = true如果 heroStyle = "background",建议每篇文章都放封面图。
Blowfish 推荐用 leaf bundle 结构:
content/posts/2026/02/09/my-post/
├── index.md
└── featured.jpg封面文件名用 featured.* 最省心,主题会自动识别。
6.3 列表、分类、标签页#
[list]
showHero = true
heroStyle = "background"
showSummary = true
showCards = true
groupByYear = false
cardView = true
[taxonomy]
showHero = true
heroStyle = "background"
[term]
showHero = true
heroStyle = "background"这几段不配的话,分类/标签页会显得特别“裸”。
7) 写第一篇文章(顺手验证目录结构)#
我习惯直接用 Hugo 命令创建文章:
hugo new content posts/2026/02/09/hello-world/index.md然后把封面图放到同目录,比如:
content/posts/2026/02/09/hello-world/
├── index.md
└── featured.jpg这一步其实在验证两件事:
- 路径组织是否符合自己的长期习惯(按日期、按专题都行)
- 主题对封面的自动识别是否正常
8) 评论系统:Giscus#
我选 Giscus 的原因很现实:免费、干净、基于 GitHub Discussions,不用自己维护后端。
8.1 前置条件#
- 准备一个 公开 GitHub 仓库
- 开启 Discussions
- 安装 giscus app
- 到 giscus 配置页 生成脚本参数
8.2 放到主题 partial 里#
创建(或修改)layouts/partials/comments.html:
<script src="https://giscus.app/client.js"
data-repo="你的用户名/仓库名"
data-repo-id="你的repo-id"
data-category="Announcements"
data-category-id="你的category-id"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="bottom"
data-theme="preferred_color_scheme"
data-lang="zh-CN"
crossorigin="anonymous"
async>
</script>8.3 让评论区跟随日/夜模式切换#
默认 preferred_color_scheme 只能跟系统主题,用户手动切换站点深浅色时,Giscus iframe 往往不会同步。
我最后加了下面这段:
<script>
function setGiscusTheme(theme) {
const iframe = document.querySelector('iframe.giscus-frame');
if (!iframe) return;
iframe.contentWindow.postMessage(
{ giscus: { setConfig: { theme: theme } } },
'https://giscus.app'
);
}
const observer = new MutationObserver(() => {
const isDark = document.documentElement.classList.contains('dark');
setGiscusTheme(isDark ? 'dark' : 'light');
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
window.addEventListener('message', (event) => {
if (event.origin !== 'https://giscus.app') return;
const isDark = document.documentElement.classList.contains('dark');
setGiscusTheme(isDark ? 'dark' : 'light');
});
</script>原理很朴素:监听 <html> 的 class,一旦出现/移除 dark,就 postMessage 通知 Giscus 改主题。
9) 阅读计数与点赞:Firebase#
Blowfish 已经内置了 Firestore 的阅读计数逻辑,接入不复杂,但步骤比较碎。
9.1 Firebase 控制台里要做的事#
- 创建项目
- 开启 Firestore Database
- 开启 Authentication 的 Anonymous 登录
- 注册一个 Web App,拿到配置参数
9.2 回填 params.toml#
[firebase]
apiKey = "你的apiKey"
authDomain = "你的项目.firebaseapp.com"
projectId = "你的项目id"
storageBucket = "你的项目.firebasestorage.app"
messagingSenderId = "你的id"
appId = "你的appId"
measurementId = "你的measurementId"
[article]
showViews = true
showLikes = true9.3 Firestore 安全规则#
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if request.auth != null;
}
}
}这里我踩了一个小坑:改完配置后页面没反应。最后发现是 hugo server 热更新没有完全重载 Firebase 相关脚本,重启一次就好了。
10) 部署到 Vercel#
本地跑通以后,再上部署就轻松很多。
10.1 .gitignore#
public/
resources/
.hugo_build.lock
.DS_Store10.2 vercel.json#
Vercel 默认 Hugo 版本可能比较旧,我这里显式指定了版本:
{
"build": {
"env": {
"HUGO_VERSION": "0.155.2"
}
}
}导入仓库时参数:
- Framework Preset:
Hugo - Build Command:
hugo --minify - Output Directory:
public
10.3 绑定自定义域名#
部署成功后,在 Vercel 的 Settings -> Domains 里加域名,然后去域名服务商配置 CNAME:
| 类型 | 名称 | 值 |
|---|---|---|
| CNAME | blog | cname.vercel-dns.com |
DNS 生效后,Vercel 会自动发证书。
11) 我最终的目录结构#
blog/
├── config/_default/
│ ├── hugo.toml
│ ├── languages.zh-cn.toml
│ ├── menus.zh-cn.toml
│ ├── markup.toml
│ └── params.toml
├── content/
│ ├── posts/
│ │ ├── _index.md
│ │ └── 2026/02/09/hello-world/
│ │ ├── index.md
│ │ └── featured.jpg
│ ├── categories/_index.md
│ └── tags/_index.md
├── layouts/partials/
│ └── comments.html
├── assets/img/
├── themes/blowfish/
├── vercel.json
└── .gitignore我踩过的坑(按出现顺序)#
- 写了主题名但页面空白:主题目录没真正下载到
themes/ - 首页背景图不显示:
layout选成了profile - 菜单还是英文:
menus.zh-cn.toml缺失或命名不对 - 阅读时间离谱:忘记
hasCJKLanguage = true - Firebase 没生效:改完配置没重启
hugo server - Vercel 构建挂掉:Hugo 版本太老,没在
vercel.json指定
收尾#
这次搭建给我的最大感受是:Hugo 本身不难,真正费时间的是“主题约定 + 第三方服务接入 + 细节排错”。
如果你也正准备搭个人博客,建议按这个顺序做:
- 先把本地页面跑起来
- 再做中文化和页面样式
- 最后再接评论、统计和部署
这样每一步都能看到明确反馈,出问题也更容易定位。
