折腾了三种博客程序之后,我最后还是回到了 Hugo
我的博客换过三次。 最早是 WordPress,插件装了一堆,功能确实全,但每次后台点一下都要等两三秒。那台 1 核 1G 的 VPS 跑 PHP 加 MySQL,内存常年飘在 90% 以上,隔三差五就被 OOM killer 干掉一次。 后来换 Hexo。静态生成,快是快,但 node_modules 那个目录,每次重装都像是在抽奖。有一次 npm install 装了两百多个包,其中一个包依赖的包又依赖别的包,最后卡在一个早就没人维护的模块上,编译失败。 再后来听人提 Hugo。我一开始觉得它就是个静态生成器,和 Hexo 差不多,没什么非换不可的理由。直到我看见有人用它构建几千篇文章只要几秒钟。 现在这个站跑在 Hugo 上,用了快一年,中间没有一次因为构建本身出问题。 装 Windows 上最省事的是用 scoop: 1 scoop install hugo-extended 一定要装 extended 版本。普通版本不支持 SCSS 和 Sass,而 Stack 主题的样式是用 SCSS 写的,装错版本会构建失败,报错信息还特别含糊。 Linux 上直接下二进制: 1 2 3 wget https://github.com/gohugoio/hugo/releases/latest/download/hugo_extended_linux-amd64.tar.gz tar -xzf hugo_extended_linux-amd64.tar.gz sudo mv hugo /usr/local/bin/ 装完验证: 1 hugo version 输出里带 extended 字样就对了。 建站 1 2 hugo new site blog cd blog 生成的目录结构是这样: 1 2 3 4 5 6 7 8 9 10 blog/ ├── archetypes ├── assets ├── content ├── data ├── i18n ├── layouts ├── static ├── themes └── hugo.toml content 是文章,static 是原样拷贝的文件,themes 是主题。assets 这个目录值得说一下,放进去的东西会经过 Hugo 的管道处理,比如 SCSS 编译、图片压缩、JS 打包。 选主题 我试过好几个,最后停在 Stack 上。 它的视觉风格比较克制,左侧固定侧边栏,文章列表卡片式,暗色模式做得挺舒服。功能上该有的都有,标签、分类、归档、搜索、代码高亮、目录,不用自己补。 装主题有两种方式,submodule 和直接下载。submodule 干净一些: 1 2 git init git submodule add https://github.com/CaiJimmy/hugo-theme-stack.git themes/hugo-theme-stack 用 submodule 之后,更新主题就是 git submodule update --remote,不会污染自己的仓库。 配置 hugo.toml 里最基本的东西: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 baseURL = "https://blog.mibase.top/" languageCode = "zh-cn" title = "明月小站" theme = "hugo-theme-stack" [pagination] pagerSize = 10 [permalinks] posts = "/p/:slug/" [markup] [markup.highlight] noClasses = false codeFences = true lineNos = false [permalinks] 那段决定了文章的 URL 结构。我设成 /p/:slug/,是因为之前见过别人的站用日期做路径,文章一多,URL 长得没法记。用 slug 更短,也更容易分享。 注意 Hugo 从 0.128 开始把 paginate 改成了 pagination.pagerSize。老教程里写的 paginate = 10 现在会报警告。 Stack 主题还需要一份自己的配置,放在 config/_default/ 下面。主题仓库里的 exampleSite 目录有一份完整的示例,复制过来改最省事。 1 cp -r themes/hugo-theme-stack/exampleSite/config/_default/* config/_default/ 然后改 config/_default/hugo.toml 里的 baseURL、title,改 params.toml 里的头像、社交链接、侧边栏设置。 写下第一篇 1 hugo new content posts/hello-world/index.md 注意这里写的是 posts/hello-world/index.md,不是 posts/hello-world.md。 这是 Hugo 的 Page Bundle 机制。用目录加 index.md 的形式,可以把图片和文章放在一起,引用的时候直接写相对路径: 1  不用管最终部署到哪个路径,Hugo 会自己处理。这个设计比把图片全塞进 static 里要好,因为文章和它的资源绑在一起,删文章的时候资源也跟着删,不会留下垃圾。 生成的 front matter 长这样: 1 2 3 4 5 --- title: "Hello World" date: 2024-11-12T16:06:00+08:00 draft: true --- draft: true 表示草稿,本地 hugo server 能看到,但构建的时候会被排除。写完改成 false 才会发布。 这个机制挺有用的。有时候写一半的东西不想让人看到,就留着 true,等写完再改。 本地预览 1 hugo server -D -D 是显示草稿。默认只监听 localhost:1313,如果要让局域网里的手机也能看: 1 hugo server -D --bind 0.0.0.0 --baseURL http://192.168.1.10:1313 --baseURL 这个参数不加的话,手机打开页面样式是乱的,因为 css 的路径还指向 localhost。 Hugo 的 server 有热重载,改文章、改模板都会立刻刷新浏览器。改配置文件有时候需要重启,因为它不监听配置文件的变化。 构建 1 hugo --minify --gc 产物在 public 目录。 --minify 会压缩 html、css、js 和 json,一般能小 20% 左右。--gc 清理掉不再被引用的缓存文件,防止 resources 目录无限膨胀。 有个参数我一开始不知道,--printPathWarnings,它会告诉你哪些页面有重复路径。文章多了之后很有用,能提前发现 slug 冲突。 部署 我把 public 目录通过 git 钩子同步到服务器,那套流程写在另一篇里了。简单说就是本地 push,服务器上的钩子执行 hugo 构建,nginx 指向产物目录。 这个方案的好处是不需要额外的 CI 服务,坏处是构建在服务器上跑,服务器得装 Hugo。我服务器上装的是 extended 版本,和本地保持一致。 目录约定 用了一段时间,我把目录固定成了这样: 1 2 3 4 5 6 7 content/ ├── page/ │ ├── about/index.md │ └── links/index.md └── posts/ ├── hello-world/index.md └── another-post/index.md posts 下面全是文章,一篇一个目录。page 下面放独立页面,比如关于、友链。Stack 主题默认就是这么分的。 分类和标签不用建目录,写在 front matter 里就行,Hugo 会自动生成列表页。 一个容易踩的坑 Hugo 的 date 字段如果没有时区,会按 UTC 解释。 我第一篇文章写的是 date: 2024-11-12 16:06:00,构建出来发现时间比实际早了八小时。后来改成带时区的格式 2024-11-12T16:06:00+08:00 才对。 这个在本地看不出来,因为 hugo server 用的是本地时区。部署到服务器上,服务器是 UTC,就露馅了。 现在回头看 三次换博客程序,每次的动机都不一样。 WordPress 是图省事,Hexo 是图轻快,Hugo 是图稳定。 现在这套东西,构建三秒,部署十秒,改一行配置立刻能看到效果。中间没有数据库,没有后台,没有需要重启的服务。它就是一堆文件。 这种简单带来的安心感,是前面两个方案没给过我的。 一些我后来才明白的细节 hugo.toml 里的 baseURL 结尾一定要带斜杠。写成 https://blog.mibase.top 而不是 https://blog.mibase.top/,生成的 rss 和 sitemap 里的链接会缺一个斜杠,有些抓取工具会因此报错。 content 目录下以 _index.md 开头的文件是列表页,不是文章。posts/_index.md 定义了文章列表页的标题和排序方式。如果你不小心把文章命名成 _index.md,它不会出现在文章列表里,反而会变成那个目录的列表页。 草稿的 draft: true 只影响构建,不影响 hugo server。只要加了 -D,草稿一样能看到。有时候我写完忘了改,本地看着好好的,部署上去发现没有这篇,才想起来。 resources/_gen 这个目录是 Hugo 的缓存,可以删,删了会重新生成。但它不要提交到 git 里,会污染 diff。.gitignore 里加上这一行: 1 2 resources/_gen/ public/ public 也不该提交。它是构建产物,每次部署都会重新生成,提交进去只会让仓库变大。 如果重新来一次 我会在第一天就把这些东西配好,而不是用了半年才补。 hugo.toml 里的 enableRobotsTXT,加上之后会自动生成 robots.txt。 [markup.goldmark.renderer] 里的 unsafe = true,加上之后才能在文章里直接写 html。默认是关闭的,是为了安全,但自己的博客自己写内容,没什么风险。 [outputs] 里给首页加上 JSON,这样才有搜索用的 index.json。Stack 的搜索依赖这个。 这些都是一次性的配置,配好之后就不用管了。但不知道的时候,会卡很久。
以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

登录后,加入这场讨论。
登录参与