Hugo+Fixit+rsync 搭建个人博客

警告
本文最后更新于 2024-05-04,文中内容可能已过时。

Hugo+Fixit+rsync 搭建个人博客

整体思路

之前通过 Hexo 搭建博客的时候,先通过 git 提交到 GitHub,然后再通过 Github Action 同步到阿里云,这个链路太长了,而且 GitHub 还时不时抽个风,经常不能做到正常更新,因此,这次切换到 Hugo 之后,决定直接在本地生成静态文件内容,然后通过 rsync 同步到阿里云,阿里云那边通过 Nginx 将静态博客代理到 80 端口和 443 端口。

先学习 hugo,再学习主题,最后开始整理博客

Hugo

Hugo 官网Hugo 官方文档GitHub - gohugoio/hugo: The world’s fastest framework for building websites.

为什么选择 Hugo?

之前通过 Hexo 搭建博客,样式自定义起来太难,而且文章发布的时候,不能根据文件系统的结构来,必须通过指定前言的 category 属性,这太复杂,太麻烦,不利于博客目录的维护,最重要的一点是,Hexo 性能太差,文章一多,发布要好久,最终决定更换静态博客框架。

选择 Hugo 是因为其足够简单,好维护,而且性能好,此外 Hugo 支持对源文件目录结构的呈现,这样我就方便博客的目录组织,相当于我用 Hugo 来为我本地文件系统下的博客文件做一个 web 页面

安装 Hugo

下载地址:Releases · gohugoio/hugo · GitHub

这里主要考虑在 Windows 下安装 Hugo,这里最好下载拓展版本,即文件名带extended的版本,功能会比较多,我这里下载的是hugo_extended_0.118.2_windows-amd64.zip

官方文档:在 Windows 上安装 Hugo

参考博客:在 Windows 上使用 Hugo 搭建博客 - 掘金

直接解压,会得到一个hugo.exe文件,注意这个文件不是拿来直接双击运行的。

创建一个名叫 Hugo 的文件夹作为 Hugo 软件目录(<hugo_root>),同时将解压出来的hugo.exe放到<hugo_root>\bin目录下,建立类似这样的目录结构

1
2
3
4
5
.
└── Hugo
    └── bin
    |   └── hugo.exe
    └── sites

然后配置环境变量,将<hugo_root>\bin这个绝对路径配置到 Path 系统环境变量中。

在 powershell 中输入hugo version,能够正常输出版本信息,则表示 hugo 安装成功,例如:

1
2
hugo version
hugo v0.118.2-da7983ac4b94d97d776d7c2405040de97e95c03d+extended windows/amd64 BuildDate=2023-08-31T11:23:51Z VendorInfo=gohugoio

简单使用

官方文档:Hugo 官方文档

参考博客:风月Qwert

首先我们要创建站点,你可以使用此命令在任意目录下创建站点,为了方便管理,我们最好统一在<hugo_root>\sites下创建站点

1
hugo new site my-site

输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
hugo new site my-site
Congratulations! Your new Hugo site was created in <hugo_root>\sites\my-site.

Just a few more steps...

1. Change the current directory to <hugo_root>\sites\my-site.
2. Create or install a theme:
   - Create a new theme with the command "hugo new theme <THEMENAME>"
   - Install a theme from https://themes.gohugo.io/
3. Edit hugo.toml, setting the "theme" property to the theme name.
4. Create new content with the command "hugo new content <SECTIONNAME>\<FILENAME>.<FORMAT>".
5. Start the embedded web server with the command "hugo server --buildDrafts".

然后,切换到站点根目录(<site_root>)下,开始创建文章

1
hugo new content posts/my-first-post.md

输出

1
Content "<site_root>\\content\\posts\\my-first-post.md" created

content 可以省略

1
hugo new posts/my-first-post.md

安装主题,直接将主题文件放到<site_root>\themes下即可,你可以下载多个主题,可以参考Hugo 官方支持的主题,也可以去 Github 上下载未被收录的主题。

然后修改<site_root>\hugo.toml文件的 theme 配置项的值为你最终决定使用的主题,然后重新部署即可。

博客文章写好之后,我们可以直接在本地预览,在<site_root>目录下,执行

1
hugo server

这会在本地创建一个高性能的 web 服务器,可用于本地预览站点。本地访问地址为:http://localhost:1313/

或者,我们也可以在<site_root>目录下执行

1
hugo

这会在<site_root>\public下生成整个站点对应的静态文件,这个文件可以拿到直接使用了,我们可以将站点静态文件托管到别的 web 服务器中,比如 GitHub。

本篇博客的做法是,通过 rsync 将文件同步到阿里云服务器,然后使用 Nginx 将其代理出去。

博客前言-front matter

markdown 文档的前言很简单,在文章开头写两排+++即可,前言类型为 TOML

前言中 date 字段为博客的创建时间,为手动添加(后期可通过脚本自动加),但是 lastmod 字段由站点的配置文件hugo.toml配置,自动填充为文件的修改时间,修改文件之后不需要手动调整时间。

注意,当我们在前言中手动指定 date 时间得时候,必须带上时区信息,比如2023-09-19T13:45:18+08:00,如果不带上时区,hugo 会默认使用默认时区的时间,也就是发布时间延后了 8 个小时,也就是说 8 个小时之后,才是文章的发布时间,当前时间还没到发布时间,这会导致文章不显示。

博客排版

关于博客的排版问题,请看《关于中文博客格式的思考》

官方教程

官方文档:Hugo 官方文档

生成静态文件

通过hugo命令生成静态文件的时候,不会先清除 public 目录。而是覆盖现有文件,但不会被删除。此行为是有意的,以防止无意中删除您可能在构建后添加到公共目录中的文件。

根据您的需要,您可能希望在每次构建之前手动清除公共目录的内容。

在 md 文件的前言中,以下四个配置的文章不会显示

  • The draft value is true
  • The date is in the future
  • The publishDate is in the future
  • The expiryDate is in the past

在调用hugo or hugo server的时候可以通过参数覆盖

1
2
3
hugo --buildDrafts    # or -D
hugo --buildExpired   # or -E
hugo --buildFuture    # or -F

也可以在站点的配置文件hugo.toml中进行全局配置,不过一般不建议这样做

前面说过,Hugo 在构建站点之前不会清除 public 目录。根据对上述四个条件的当前评估,在构建之后,您的公共目录可能包含来自先前构建的无关文件。一种常见的做法是在每次构建之前手动清除公共目录的内容,以删除草稿、过期和未来的内容。

文件夹挂载

Hugo 创建了一个联合文件系统,允许您将两个或多个目录挂载到同一位置。通过这种方式,我们可以拓展站点的多个目录 archetypesassetscontentdatai18nlayouts, and static.比如content目录,

当两个或多个文件具有相同的路径时,优先级顺序遵循挂载的顺序。例如,

1
2
3
4
5
6
7
[module]
[[module.mounts]]
    source = 'content'
    target = 'content'
[[module.mounts]]
    source = '/home/user/shared-content'
    target = 'content'

如果站点的conte目录和/home/user/shared-content目录下都有“books/spring.md”这个文件,那么,/home/user/shared-content目录下下的将被忽略。

主题的文件结构跟站点的文件结构基本上一模一样,这有个好处,就是通过上面所说的这个特性,我们可以方便地覆盖主题中的模板。

配置站点

除了使用单个站点配置文件外,还可以使用 configDir 目录 (默认为 config/) 来维护更容易组织的和特定于环境的设置。这个有点像 Maven 的 profile,可以根据构建参数,以不同的配置进行不同的打包,这个我们用不上。

站点配置的所有配置项:configuration-settings

配置标记语言到 HTML 的转换

默认配置

内容管理

Hugo 假设用于组织源内容的目录结构与用于组织呈现的站点的目录结构相同。这其实挺好的,比如content下有两个文件夹,一个是about,一个是blog,那么访问http://localhost:1313/post/就只会看到 post 文件夹下的所有 md 文件,访问http://localhost:1313/about/就只会看到 about 文件夹下的所有 md 文件,

站点的内容的顶层 (即content/<DIRECTORIES>) 在 Hugo 中是特殊的,被认为是用来决定布局等的内容类型。简单显示如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.
└── content
    └── about
    |   └── index.md  // <- https://example.com/about/
    ├── posts
    |   ├── firstpost.md   // <- https://example.com/posts/firstpost/
    |   ├── happy
    |   |   └── ness.md  // <- https://example.com/posts/happy/ness/
    |   └── secondpost.md  // <- https://example.com/posts/secondpost/
    └── quote
        ├── first.md       // <- https://example.com/quote/first/
        └── second.md      // <- https://example.com/quote/second/

要了解有关section的更多信息,包括如何嵌套它们,请参阅下一小节。

Section

官方文档:sections

section 是content文件夹下的顶级内容目录,或任何带有_index.md文件的内容目录。

排除指定文件或者文件夹

官方文档:Ignore content and data files when rendering

例如,想要忽略以.foo.boo结尾的文件,在<site_root>\hugo.toml中配置

1
ignoreFiles = ['\.foo$', '\.boo$']

总结

这个官方文档的内容和排版,看得实在是难受,算了,不看了。后面有需要再看别人的博客。

参考博客:风月Qwert

我的需求

我基本上就两种组织博客内容的场景需求,

  • 列表:以列表页面的形式展示博客,这些博客之间是没有上下文关系的,同时,列表中的某一项还可能是一个专题链接,点击进去之后打开的另一个列表页面。

    即:从一个链接点进去,应该是一个列表页面,列表页面的某一项再点进去,可能还是列表,也可能直接打开一篇博客。

  • 书籍的内容目录:展示同一本书或者一个系列的教程中的内容,先展示一个目录页面(支持多级),目录中按照顺序展示章节,点击目录中的条目直接进入相应章节

    即:从一个链接点进去,是一个目录页面,点击链接直接进入相应章节的博客。

解决方案

列表:

文件系统的每一层文件夹中得有_index.md文件才能有列表页面,同时_index.md的前言中的title即为这个列表的名称,会在父文件夹的列表页面中显示,如果没有指定title,默认为空字符串,不会在父列表页面中显示。

书籍的内容目录:

把目录文件和博客文件放两个文件夹中,目录文件夹为 catalog,里面就一个index.md文件,这就是目录页面的内容,目录文件需要配置前言中的 title 属性,这个属性为书籍/系列文章名称,内容文件夹为 content,里面除了博客文件以外,需要放一个_index.md文件,这个文件也需要配置前言中的 title 属性,这个属性值跟 catalog 文件夹下中的index.md的 title 属性保持一致。content 文件夹下支持嵌套,嵌套的方式跟列表一样。

除了 catalog 和 content,还可以再创建一个 resources 文件夹,里面需要放一个_index.md文件,这个文件也需要配置前言中的 title 属性,这个属性值跟 catalog 文件夹下中的index.md的 title 属性保持一致,这个文件夹主要将一些书籍相关的文件放到这里,主要是放一些静态文件,比如博客文章中涉及到练习代码或者脚本,注意,不要将太大体积的文件放到这里,比如一本书的 PDF,动不动几十几百兆,一下子让整个静态博客的体积撑得特别大,这是不行了,一般这种文件我们都会放到统一的下载地址,比如虾说博客得文件下载地址,我们要尽量保持静态博客的轻量,这样不管是部署还是更新都会很快。

本地其他配置

为了将整个站点的信息进行备份,可以直接配置网盘同步目录为<site_root>,这样就可以备份站点的配置,自定义的部分,还有所有的博客文章。

我本地使用的是 Onedrive,只能将站点的内容放到 Onedrive 目录下,然后通过软链接链接到<hugo_root\sites>目录下。

以管理员身份打开 cmd,执行

1
mklink /J <site_root>\xiashuo <onedrive_path>\XiaShuoBlog

想要删除软连接,直接删除 xiashuo 文件夹,或者执行

1
rmdir <site_root>\xiashuo

FixIt 主题

Github 地址:GitHub - hugo-fixit/FixIt: 🔧 A clean, elegant but advanced blog theme for Hugo 一个简洁、优雅且高效的 Hugo 主题

官方教程:快速上手 - FixIt

跟着官方教程,基本上都能快速上手使用,其中最重要的是完整配置 - FixIt,这里面讲解了此主题的所有配置,用户可根据自己的实际情况来修改特定的配置,其中在主题配置小节,作者对每一项配置都做了中文解释,用户可以直接将其复制到<site_root>\hugo.toml中。

自定义主题页面

以列表页为例,Fixit 主题的列表页的位置是<site_root>\themes\FixIt\layouts\_default\section.html,现在我们想自定义这个页面,方式很简单,将这个页面复制到<site_root>\layouts\_default\section.html中,然后生效的就是后者了,然后修改<site_root>\layouts\_default\section.html中的内容即可自定义。

其他的页面也都可以用这种方式自定义。

搜索

选择Lunr.js,全文检索,虽然查询速度慢,但是至少能查出来

自带的fuse.js搜索功能不好用,搜不出来啥,但是有的时候好像又搜地出来。好像是跟搜索项匹配的标题都能准确搜索出来,但是正文内容不一定能搜索出来

Lunr.js

官方文档:Searching : Lunr

一些基本概念:

  • 相关性:也叫匹配得分,主要采用Okapi BM25 - Wikipedia算法,简而言之,一个搜索词在单个文档中出现的次数越多,该搜索词增加文档分数的次数就越多,但是一个搜索词在整个文档集合中出现的次数越多,该搜索词增加文档分数的次数就越少。

  • 编辑距离:编辑距离为 1 的意思是,一个词经过添加或删除或更改或调换 1 个字符就能跟你查询的词匹配。例如你在搜索 foo,boo 只需要一次编辑 (用 b 替换 f) 就可以匹配,但是 boot 不需要,因为它还需要在末尾添加一个 t。

搜索语法

  • 当我们输入foo bar的时候,这是搜索两个词,文档中只要包含一个词,就会匹配,也就是说在同时搜索多个词的时候,词与词之间是或的关系

  • 设置某个搜索词必须出现和必须不出现,默认情况下,Lunr 使用逻辑或将多个词组合在一起进行搜索。文档必须至少有一个匹配词,其实,我们可以指定一个词必须在匹配文档中出现,或者在匹配文档中不存在。

    为了表明某个搜索语必须出现在匹配文档中,该搜索语的前缀应该加上加号 (+);为了表明某个术语必须不存在,该术语的前缀应该加上减号 (-)。如果没有这两种前缀,则在匹配文档中是否存在术语是可选的。

    例如搜索+foo bar -baz,foo 必须存在,baz 必须不存在,bar 可存在可不存在。

  • 搜索词支持通配符*,可以出现在查询的词的任何位置,比如*foofo*ofoo*。不过通配符会降低搜索性能

  • 将搜索词的搜索范围限制到指定字段,比如限制在标题中搜索 foo,title:foo,这个字段必须是在创建索引的时候指定的字段,默认情况下会在所有字段中查询。搜索范围的限定只针对一个搜索词,可以跟其他搜索规则配合title:foo* bar

  • 指定搜索词的权重,任何与配置了权重的搜索词匹配的文档都将获得更高的相关性分数,并在结果中出现更高的位置,比如搜索foo^10 bar,此时,foo 会有 bar 10 倍的权重,产生 10 倍的相关性分数

  • 当我们不知道完整的搜索词的时候,可以指定编辑距离来进行模糊查询,例如,搜索foo~1的时候,会匹配所有的与 foo 的编辑距离为 1 的词。

自动化发布

为了方便博客的发布,我写一个 python 脚本,通过 pyinstall 打包成sync.exe,放到<site_root>\content目录下,双击即可运行。非常方便

记得在<site_root>\hugo.toml中配置排除所有的 exe 文件,减少静态站点的体积

1
2
# 排除所有 exe
ignoreFiles = ['\.exe$']

这个脚本做的工作为:

  • 格式化 md 博客,

    • 自动添加基本前言(front matter)信息

    • 自动修正博客排版,主要是添加空格

  • <site_root>目录下执行hugo命令

  • 通过 rsync 同步到阿里云

    具体如何同步,请看《使用 rsync 从 Windows 到 Linux 进行同步》

脚本的具体内容,请看sync.py

除了手动双击sync.exe来运行之外,我还将sync.exe的快捷方式放到了 Windows 系统的开机自启目录下(C:\Users\<user_name>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup),这样,sync.exe会在每次电脑开机的时候运行。

sync.exe的具体执行逻辑是:扫描当前路径下所有的 md 文件,如果发现没有前言,则会自动添加前言(仅添加 title 和 date 属性),并进行格式化操作,添加必要的空格,如果 md 文件已经有了前言,则跳过。这就要求我们,在对已经有了前言的 md 文档的内容进行更新之后,需要手动维护前言和添加空格的格式化操作,可以用 vscode 打开 md 文件,然后 Ctrl + s 保存即可(vscode 安装autocorrect插件之后的效果)。

sync.py中存在参数always_update_front_matter,默认 0,可以改为 1 然后重新打一个sync.exe,放到<site_root>\content目录下执行一次,强行为所有的博客进行添加空格的操作,不过这样的话,所有的博客的修改时间都会更新。为了保证博客的修改时间仅代表内容的修改时间,一般不这么干。

内容迁移

整理 MyLearnning

整理 D:\OneDrive\Document\MyShare\XiaShuoBlog\source_posts

整理 D:\OneDrive\Document\MyShare\blogAbout2Post

整理 D:\OneDrive\Document\MyLearnning\BookNote

慢慢整理吧,全都整理完就好啦,TODO

这是个大工程,以前偷懒,现在偷不得懒了。

0%