Building a Personal Blog with Hugo (2)

Picking up where we left off, the previous post briefly covered how to install and use Hugo. But stopping right there would feel a bit thin.

To ensure a good writing experience and stay focused on producing content, it’s worth spending more time up front — only then will you get twice the result with half the effort later. So this post goes deeper into optimization.

I’d like to thank the bigshot Sulv for the generous help. His blog: https://www.sulvblog.cn/


Hugo Directory Overview

First, let me introduce the directories that Hugo generates when creating a new site and what they do, so you have a sense of direction for the operations below. A website generated with Hugo contains the following files and folders:

  • archetypes: stores .md template files; this folder takes priority over the /archetypes folder in the theme
  • config.toml: the configuration file
  • content: stores all the content of the site
  • data: stores data files for templates to use
  • layouts: stores .html templates; this folder takes priority over the /layouts folder in the theme
  • static: stores static files such as images, css, and js; files in this directory are copied directly to /public, and this folder takes priority over the /static folder in the theme
  • themes: stores themes
  • public: stores the generated static files after running the hugo command

Steps

1. Edit config.yml

The config.yml file is the configuration for the whole site. It tells Hugo how to build the site, such as global parameters and menus. The file defaults to toml format; in the previous article, the -f yml argument in the Generate Site section of the commands changed the default configuration file to yaml format.

  • The default configuration provided by the official docs: Sample config.yml
  • For the meaning of the parameters, refer to the theme Wiki: Features, Variables, as well as the relevant content in the official Hugo docs: Configure Hugo.
  • The configuration file used here is the one given by Sulv; thanks again to Sulv. The content of the configuration file needs to be modified according to your actual situation; the main thing is to change site identifiers such as links and titles to your own. Also, logo.gif should be placed at static/img/logo.gif in the site’s root directory.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
baseURL: https://www.sulvblog.cn
# baseURL: https://www.sulvblog.cn  # 绑定的域名
languageCode: zh-cn # en-us
title: Sulv's Blog
theme: PaperMod # 主题名字,和themes文件夹下的一致

enableInlineShortcodes: true
enableEmoji: true # 允许使用 Emoji 表情,建议 true
enableRobotsTXT: true # 允许爬虫抓取到搜索引擎,建议 true

hasCJKLanguage: true # 自动检测是否包含 中文日文韩文 如果文章中使用了很多中文引号的话可以开启

buildDrafts: false
buildFuture: false
buildExpired: false

#googleAnalytics: UA-123-45 # 谷歌统计
# Copyright: Sulv

paginate: 10    # 首页每页显示的文章数

minify:
    disableXML: true
    # minifyOutput: true

permalinks:
  post: "/:title/"
  # post: "/:year/:month/:day/:title/"

defaultContentLanguage: zh # 最顶部首先展示的语言页面
defaultContentLanguageInSubdir: true

languages:
    zh:
      languageName: "Chinese"
      # contentDir: content/english
      weight: 1
      profileMode:
        enabled: true
        title: (〃'▽'〃)
        subtitle: "🧨学而时习之,不亦说乎?有朋自远方来,不亦乐乎?</br>👏🏼欢迎光临素履(Sulv)的博客</br>👇联系方式"
        imageUrl: "img/logo.gif"
        imageTitle: 
        imageWidth: 150
        imageHeight: 150
        buttons:
          - name: 👨🏻‍💻技术
            url: posts/tech
          - name: 📕阅读
            url: posts/read
          - name: 🏖生活
            url: posts/life
          # - name: 🌹素履的博客
          #   url: https://www.xyming108.top
      menu:
        main:
          - identifier: search
            name: 🔍搜索
            url: search
            weight: 1
          - identifier: home
            name: 🏠主页
            url: /
            weight: 2
          - identifier: posts
            name: 📚文章
            url: posts
            weight: 3
          # - identifier: tech
          #   name: 👨🏻‍💻技术文章
          #   url: posts/tech
          #   weight: 5
          # - identifier: life
          #   name: 🏖记录生活
          #   url: posts/life
          #   weight: 6
          - identifier: archives
            name: ⏱时间轴
            url: archives/
            weight: 20
          # - identifier: categories
          #   name: 🧩分类
          #   url: categories
          #   weight: 30
          - identifier: tags
            name: 🔖标签
            url: tags
            weight: 40
          - identifier: about
            name: 🙋🏻‍♂️关于
            url: about
            weight: 50
          - identifier: links
            name: 🤝友链
            url: links
            weight: 60

outputs:
    home:
        - HTML
        - RSS
        - JSON

params:
    env: production # to enable google analytics, opengraph, twitter-cards and schema.
    # description: "这是一个纯粹的博客......"
    author: Sulv
    # author: ["Me", "You"] # multiple authors

  
    defaultTheme: auto  # defaultTheme: light or  dark 
    disableThemeToggle: false
    DateFormat: "2006-01-02"
    ShowShareButtons: true
    ShowReadingTime: true
    # disableSpecialistPost: true
    displayFullLangName: true
    ShowPostNavLinks: true
    ShowBreadCrumbs: true
    ShowCodeCopyButtons: true
    hideFooter: false # 隐藏页脚
    ShowWordCounts: true
    VisitCount: true

    ShowLastMod: true #显示文章更新时间

    ShowToc: true # 显示目录
    TocOpen: true # 自动展开目录

    comments: true
    
    socialIcons:
        - name: github
          url: "https://github.com/xyming108"
        - name: twitter
          url:  "img/twitter.png"
        - name: facebook
          url: "https://www.facebook.com/profile.php?id=100027782410997"
        - name: instagram
          url: "img/instagram.png"
        - name: QQ
          url: "img/qq.png"
        - name: WeChat
          url: "img/wechat.png"
        # - name: Phone
        #   url: "img/phone.png"
        - name: email
          url: "mailto:[email protected]"
        - name: RSS
          url: "index.xml"

    # editPost:
    #     URL: "https://github.com/adityatelange/hugo-PaperMod/tree/exampleSite/content"
    #     Text: "Suggest Changes" # edit text
    #     appendFilePath: true # to append file path to Edit link

    # label:
    #     text: "Home"
    #     icon: icon.png
    #     iconHeight: 35

    # analytics:
    #     google:
    #         SiteVerificationTag: "XYZabc"

    assets:
        favicon: "img/logo.gif"
        favicon16x16: "img/logo.gif"
        favicon32x32: "img/logo.gif"
        apple_touch_icon: "logo.gif"
        safari_pinned_tab: "logo.gif"

    # cover:
    #     hidden: true # hide everywhere but not in structured data
    #     hiddenInList: true # hide on list pages and home
    #     hiddenInSingle: true # hide on single page

    fuseOpts:
        isCaseSensitive: false
        shouldSort: true
        location: 0
        distance: 1000
        threshold: 1
        minMatchCharLength: 0
        keys: ["title", "permalink", "summary"]

    twikoo:
      version: 1.4.11

taxonomies:
    category: categories
    tag: tags
    series: series

markup:
    goldmark:
        renderer:
            unsafe: true # HUGO 默认转义 Markdown 文件中的 HTML 代码,如需开启的话
    highlight:
        # anchorLineNos: true
        codeFences: true  
        guessSyntax: true
        lineNos: true
        # noClasses: false
        # style: monokai
        style: darcula

        # codeFences:代码围栏功能,这个功能一般都要设为 true 的,不然很难看,就是干巴巴的-代码文字,没有颜色。
        # guessSyntax:猜测语法,这个功能建议设置为 true, 如果你没有设置要显示的语言则会自动匹配。
        # hl_Lines:高亮的行号,一般这个不设置,因为每个代码块我们可能希望让高亮的地方不一样。
        # lineNoStart:行号从编号几开始,一般从 1 开始。
        # lineNos:是否显示行号,我比较喜欢显示,所以我设置的为 true.
        # lineNumbersInTable:使用表来格式化行号和代码,而不是 标签。这个属性一般设置为 true.
        # noClasses:使用 class 标签,而不是内嵌的内联样式

privacy:
    vimeo:
        disabled: false
        simple: true

    twitter:
        disabled: false
        enableDNT: true
        simple: true

    instagram:
        disabled: false
        simple: true

    youtube:
        disabled: false
        privacyEnhanced: true

services:
    instagram:
        disableInlineCSS: true
    twitter:
        disableInlineCSS: true
        

2. Configure the 🔍 Search Page

Original reference: Search Page

PaperMod uses Fuse.js Basic to implement the search functionality

The search functionality is already enabled in the configuration file. You need to create a search.md containing the following content in the content/ directory in the site’s root. Additionally, you can customize it to your needs by referring to the theme documentation.

1
2
3
4
5
6
7
8
---
title: "🔍搜索" # in any language you want
layout: "search" # is necessary
# url: "/archive"
# description: "Description for Search"
summary: "search"
placeholder: "搜索框内的默认显示,自行修改"
---

To hide a specific page from search, add the following to its front matter (the content enclosed by the two --- lines at the top):

1
searchHidden: true

3. Configure the ⏱ Timeline Page

Original reference: Archives Layout

The ⏱ Timeline page is the Archives Layout. The archive functionality is already enabled in the configuration file. You need to create an archives.md containing the following content in the content/ directory in the site’s root. Additionally, you can customize it to your needs by referring to the theme documentation.

1
2
3
4
5
6
---
title: "⏱ Timeline"
layout: "archives"
url: "/archives/"
summary: archives
---

4. Configure the 🔖 Tags Page

  • The default language set in config.yml is Chinese, and support for other languages is not enabled. If needed, you can customize it yourself by referring to the official docs: Translations, Multilingual Mode . However, the 🔖 Tags page is not controlled by this setting, so you need to modify the corresponding html template yourself.
  • Also, regarding the default language: the theme supports multilingual, meaning it provides some translations for its built-in page templates, e.g. Home -> 主页. The files that provide multilingual support are in themes/PaperMod/i18n/, where zh.yaml is Simplified Chinese.
  • To prevent your modified files from being reverted when updating the theme, make the modifications in the site’s root directory, so they can override the theme’s original files.

Once you understand the three points above, you can start the following modifications.

4.1. Modify the default translations

Create an i18n/ folder in the site’s root, copy themes/PaperMod/i18n/zh.yaml to i18n/zh.yaml, and edit it

1
2
3
mkdir i18n \
  && cp themes/PaperMod/i18n/zh.yaml i18n/zh.yaml \
    && nano i18n/zh.yaml

Change it to the following content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- id: prev_page
  translation: "上一页"

- id: next_page
  translation: "下一页"

- id: read_time
  translation:
    one : "1 分钟"
    other: "{{ .Count }} 分钟"

- id: toc
  translation: "目录"

- id: translations
  translation: "语言"

- id: home
  translation: "🏠主页"

- id: edit_post
  translation: "编辑"

- id: code_copy
  translation: "复制"

- id: code_copied
  translation: "已复制!"

4.2. Modify the html template

The template file for 🔖 Tags is themes/PaperMod/layouts/_default/terms.html. Follow the same steps as above: create a layouts/_default/ folder in the site’s root, copy themes/PaperMod/layouts/_default/terms.html to layouts/_default/terms.html, and edit it

1
2
3
mkdir layouts/_default \
  && cp themes/PaperMod/layouts/_default/terms.html layouts/_default/terms.html \
    && nano layouts/_default/terms.html

Change it to the following content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{{- define "main" }}

{{- if .Title }}
<header class="page-header">
    {{- if eq .Title "Categories" }}
    <h1>🧩{{ .Title }}</h1>
    {{- end }}
    {{- if eq .Title "Tags" }}
        <h1>🔖{{ "标签" }}</h1>
        <!-- <h1>🔖{{ .Title }}</h1> -->
    {{- end }}
    <!-- <h1>{{ .Title }}</h1> -->
    {{- if .Description }}
    <div class="post-description">
        {{ .Description }}
    </div>
    {{- end }}
</header>
{{- end }}

<!-- 原始 -->

<ul class="terms-tags">
    {{- $type := .Type }}
    {{- range $key, $value := .Data.Terms.Alphabetical }}
    {{- $name := .Name }}
    {{- $count := .Count }}
    {{- with $.Site.GetPage (printf "/%s/%s" $type $name) }}
    <li>
        <a href="{{ .Permalink }}">{{ .Name }} <sup><strong><sup>{{ $count }}</sup></strong></sup> </a>
    </li>
    {{- end }}
    {{- end }}
</ul>

{{- end }}{{/* end main */ -}}

5. Customize the front matter

Hugo provides a default template for generating articles. When you use hugo new content/posts/demo.md to generate a new page, it provides the most basic front matter, located at archetypes/default.md, with the following content:

1
2
3
4
5
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

This template only contains the three required parameters: title, date, and draft (whether it is a draft?). For writing convenience, here is a template provided by the bigshot Sulv, which I have slightly modified.

Create a new archetypes/post.md and edit it,

1
nano archetypes/post.md

Paste the following content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
lastmod: {{ .Date }}
author: ["作者"]
categories:
- 分类1
- 分类2
tags:
- 标签1
- 标签2
# summary->在列表页展现的摘要内容,自动生成,内容默认前70个字符,可通过此参数自定义,一般无需专门设置
summary: ""
# description->需要自己编写的文章描述,是搜索引擎呈现在搜索结果链接下方的网页简介,建议设置
description: ""
weight: # 输入1可以顶置文章,用来给文章展示排序,不填就默认按时间排序
slug: ""
draft: false # 是否为草稿
comments: true
showToc: true # 显示目录
TocOpen: true # 自动展开目录
hidemeta: false # 是否隐藏文章的元信息,如发布日期、作者等
disableShare: true # 底部不显示分享栏
showbreadcrumbs: true #顶部显示当前路径
cover:
    image: ""
    caption: ""
    alt: ""
    relative: false
---

此处内容将会出现在摘要(summary)里

<!--\more--> # 此处的“\”用于转义,否则无法正常显示,实际使用须删去。

此处开始为正文

6. Configure the 🙋🏻‍♂️ About Page

In the previous section we configured the article generation template. Below I’ll show how to use the template to generate the 🙋🏻‍♂️ About page (about.md).
First, generate about.md in the content/ directory

1
hugo new --kind post content/about.md

You’ll see an about.md with the template content; modify the front matter parameters according to the comments and your own needs.
Then, starting at the very bottom, write the body using Markdown syntax to complete your own 🙋🏻‍♂️ About page

7. Content Management from Novice to Pro

The Hugo Directory Overview section introduced the paths in the site and their purposes. So where do we put our finished articles? The answer is the content/ directory.

The official docs Content Management explain in detail everything you need to know about managing blog content. Below I’ll cover what we need to know for now.

  • Once the blog is live, everything we see is Markdown files in the content/ directory. The content/ directory is the top-level directory that manages all the site’s content; it generally serves as the site’s homepage, with an access URL of: http://example.com. Also, files like about.md, archives.md, etc. are usually stored here.
  • For a Markdown file created in the content/ directory, e.g. about.md, its access URL is: http://example.com/about/ . You may notice it has no extension. In Hugo the default behavior is to present content using Pretty URLs (Pretty URLs), i.e. the “.html” extension is omitted from links. If you have a peculiar taste, you can set uglyurls: true in config.yml to enable the Ugly URLs (Ugly URLs) environment variable, and the access URL will become: https://example.com/about.html/
  • For a directory created in the content/ directory, e.g. posts (by convention we store our blog articles under this path), its access URL is: http://example.com/posts/ , and other directories work the same way.
    • In my blog, I created the following paths:
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      
      content
      ├── about.md
      ├── archives.md
      ├── posts
      │   ├── _index.md
      │   ├── life
      │   │   └── _index.md
      │   ├── read
      │   │   └── _index.md
      │   └── tech
      │       ├── 1st.md
      │       ├── 2nd.md
      │       └── _index.md
      └── search.md
      
      Where posts/life/ corresponds to 🏖 Life; posts/read/ corresponds to 📕 Reading; posts/tech/ corresponds to 👨🏻‍💻 Tech.
    • Let me emphasize here: _index.md. The official reference is Index Pages. In short, this file exists as a custom index page. Here’s one I use as a reference:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      ---
      title: "📚文章"
      # description: "知识是学习来的,经验是总结来的。"
      hidemeta: true # 是否隐藏文章的元信息,如发布日期、作者等
      ---
      
      知识是学习来的,经验是总结来的。
      
      <!--\more--> # 此处的“\”用于转义,否则无法正常显示,实际使用须删去。
      

Final Words

At this point, the basic configuration of Hugo is essentially complete, and you can focus on creating. If you need more freedom or have other needs, you can also do deep customization. In the next section, I’ll introduce some Hugo writing workflows, a few additional usage tips, and share the script I use to back up Hugo with a Tianyi cloud drive.

References