1 - 命令行(Command)

记录 Hugo 常用命令行

命令行顾名思义,这里不赘述其具体用法 (具体用法参考hugo -h官方文档);这里仅做分类,作用快速查找。

帮助

1hugo compeletion [bash/fish/powershell/zsh]
2hugo gen [doc/man]

环境

1hugo version
2hugo env
3hugo config 
4hugo config mounts
5hugo list [all/drafts/expired/future]

构建 🔥

1hugo new site <project_name>
2hugo new theme <theme_name>
3hugo new <page_name>

测试 🔥

1hugo serve --buildDrafts --buildExpired --buildFuture 

模块

1hugo mod [graph]
2hugo mod init 
3...

转换/迁移

1hugo import [jekyll]
2hugo convert [toJSON/toTOML/toYAML] # 不建议使用

发布 🔥

1hugo --cleanDestinationDir --minify
2hugo deploy

2 - 目录结构(Directory-structure)🔥

记录 Hugo 如何划分目录功能。

官方文档: https://gohugo.io/getting-started/directory-structure/

项目目录结构:

example/
├── archetypes/
│   └── default.md
├── assets/
├── content/
├── data/
├── layouts/
├── public/
├── static/
├── themes/
└── config.toml

config.toml —— 存放配置

官方文档:

配置可以统一记录在一个文件中:

 1contentDir = "content/en"
 2defaultContentLanguage = "en"
 3defaultContentLanguageInSubdir = false
 4[[menu.main]]
 5    name = "GitHub"
 6    weight = 50
 7    url = "https://github.com/google/docsy/"
 8    pre = "<i class="fa-brands fa-github"></i>"
 9    post = "<span class='alert'>New!</span>"
10[languages]
11[languages.en]
12title = "Docsy"
13description = "Docsy does docs"
14languageName ="English"
15# Weight used for sorting.
16weight = 1
17[languages.no]
18title = "Docsy"
19description = "Docsy er operativsystem for skyen"
20languageName ="Norsk"
21contentDir = "content/no"
22time_format_default = "02.01.2006"
23time_format_blog = "02.01.2006"
 1contentDir: content/en
 2defaultContentLanguage: en
 3defaultContentLanguageInSubdir: false
 4menu:
 5  main:
 6    - name: GitHub
 7      weight: 50
 8      url: 'https://github.com/google/docsy/'
 9      pre: <i class="fa-brands fa-github"></i>
10      post: <span class='alert'>New!</span>
11languages:
12  en:
13    title: Docsy
14    description: Docsy does docs
15    languageName: English
16    weight: 1 # used for sorting
17  'no':
18    title: Docsy
19    description: Docsy er operativsystem for skyen
20    languageName: Norsk
21    contentDir: content/no
22    time_format_default: 02.01.2006
23    time_format_blog: 02.01.2006
 1{
 2  "contentDir": "content/en",
 3  "defaultContentLanguage": "en",
 4  "defaultContentLanguageInSubdir": false,
 5    "menu": {
 6    "main": [
 7      {
 8        "name": "GitHub",
 9        "weight": 50,
10        "url": "https://github.com/google/docsy/",
11        "pre": "<i class="fa-brands fa-github"></i>",
12        "post": "<span class='alert'>New!</span>"
13      }
14    ]
15  },
16  "languages": {
17    "en": {
18      "title": "Docsy",
19      "description": "Docsy does docs",
20      "languageName": "English",
21      "weight": 1
22    },
23    "no": {
24      "title": "Docsy",
25      "description": "Docsy er operativsystem for skyen",
26      "languageName": "Norsk",
27      "contentDir": "content/no",
28      "time_format_default": "02.01.2006",
29      "time_format_blog": "02.01.2006"
30    }
31  }
32}

新版配置文件名使用 hugo.yaml/hugo.toml/hugo.json。当然,向上兼容。

为了便于管理,也可以将其拆分为多个文件。 另一个好处是可以配置多个场景(profile)以应对不同情况的配置需求,比如production是发布时使用的配置,staging是本地预览时使用的配置。

 1├── config
 2│   ├── _default
 3│   │   ├── config.toml
 4│   │   ├── languages.toml
 5│   │   ├── menus.en.toml
 6│   │   ├── menus.zh.toml
 7│   │   └── params.toml
 8│   ├── production
 9│   │   ├── config.toml
10│   │   └── params.toml
11│   └── staging
12│       ├── config.toml
13│       └── params.toml

archetypes —— 文章原型

通过 hugo new <文件名>.md 命令生成的 .md 文章会自动填充 archetypes/default.md 模板定义的内容。

assets —— 待加工资源

这里的文件可以被 Hugo Pipes 调用并处理,然后发布到 public/ 目录。

官方文档:

static —— 静态资源

存放静态文件,比如图片、CSS、JS

区别 assets/static/ 目录:

  • assets/ —— 经过 Hugo Pipes 加工后才移动
  • static/ —— 原封不动的移动到 public/ 目录

layouts —— 页面布局

转跳: 页面布局

themes —— 主题

一般将主题放进去后就不用管了。

Tips: 遇到问题可以直接看主题下的源码,有时比翻文档直接、快捷。前提需要对 Hugo 机制熟悉。

├── layouts
└── themes
    └── mytheme
        └── layouts
            ├── 404.html             // 404页面模板
            ├── _default
            │   ├── baseof.html      // 默认的基础模板页, 使用的方式是'拼接', 而不是'继承'.
            │   ├── list.html        // 列表模板  
            │   └── single.html      // 单页模板
            ├── index.html           // 首页模板
            └── partials             // 局部模板, 通过partial引入
                ├── footer.html
                ├── header.html
                └── head.html      

content —— 内容管理

转跳: 内容管理

data —— 站点数据

官方文档:

这里定义的数据可以在 config.yaml 中使用或者在 layouts/ 中通过 $.Site.Data 获取。

e.g.

1discography = ['1974 - Modern American Music … Period! The Criteria Sessions', '1974 - Jaco', '1976 - Jaco Pastorius', '1981 - Word of Mouth', '1981 - The Birthday Concert (released in 1995)', '1982 - Twins I & II (released in 1999)', '1983 - Invitation', '1986 - Broadway Blues (released in 1998)', '1986 - Honestly Solo Live (released in 1990)', '1986 - Live In Italy (released in 1991)', "1986 - Heavy'n Jazz (released in 1992)", '1991 - Live In New York City, Volumes 1-7.', '1999 - Rare Collection (compilation)', '2003 - Punk Jazz: The Jaco Pastorius Anthology (compilation)', '2007 - The Essential Jaco Pastorius (compilation)']
 1discography:
 2- 1974 - Modern American Music … Period! The Criteria Sessions
 3- 1974 - Jaco
 4- 1976 - Jaco Pastorius
 5- 1981 - Word of Mouth
 6- 1981 - The Birthday Concert (released in 1995)
 7- 1982 - Twins I & II (released in 1999)
 8- 1983 - Invitation
 9- 1986 - Broadway Blues (released in 1998)
10- 1986 - Honestly Solo Live (released in 1990)
11- 1986 - Live In Italy (released in 1991)
12- 1986 - Heavy'n Jazz (released in 1992)
13- 1991 - Live In New York City, Volumes 1-7.
14- 1999 - Rare Collection (compilation)
15- '2003 - Punk Jazz: The Jaco Pastorius Anthology (compilation)'
16- 2007 - The Essential Jaco Pastorius (compilation)
 1{
 2   "discography": [
 3      "1974 - Modern American Music … Period! The Criteria Sessions",
 4      "1974 - Jaco",
 5      "1976 - Jaco Pastorius",
 6      "1981 - Word of Mouth",
 7      "1981 - The Birthday Concert (released in 1995)",
 8      "1982 - Twins I \u0026 II (released in 1999)",
 9      "1983 - Invitation",
10      "1986 - Broadway Blues (released in 1998)",
11      "1986 - Honestly Solo Live (released in 1990)",
12      "1986 - Live In Italy (released in 1991)",
13      "1986 - Heavy'n Jazz (released in 1992)",
14      "1991 - Live In New York City, Volumes 1-7.",
15      "1999 - Rare Collection (compilation)",
16      "2003 - Punk Jazz: The Jaco Pastorius Anthology (compilation)",
17      "2007 - The Essential Jaco Pastorius (compilation)"
18   ]
19}

layouts/home.html

1<ul>
2  {{ range $.Site.Data.jazz.bass.jacopastorius.discography }}
3  <li>{{ . }}</li>
4  {{ end }}
5</ul>

public —— 发布目录

用于存放生成的站点文件

 1public/
 2├── categories/
 3│   ├── index.html
 4│   └── index.xml  <-- RSS feed for this section
 5├── post/
 6│   ├── my-first-post/
 7│   │   └── index.html
 8│   ├── index.html
 9│   └── index.xml  <-- RSS feed for this section
10├── tags/
11│   ├── index.html
12│   └── index.xml  <-- RSS feed for this section
13├── index.html
14├── index.xml      <-- RSS feed for the site
15└── sitemap.xml

3 - 标记解析(Markup)

定义 Hugo 的 Markdown 解析器及其解析风格(包括:特殊字符解析规则、章节样式、代码高亮样式和表格样式)。

官方文档: https://gohugo.io/getting-started/configuration-markup/ (配置看这里)

所谓 “标记解析” 就是将 .md 文件中的各种特殊字符、特殊格式转换为 .html 文件形式。可以理解为渲染 .md 文件。

Goldmark —— 解析 markdown 标记

Hugo 默认使用 goldmark 解析 markdown 标记。

下面记录常用配置

unsafe

是否允许渲染 html 代码块。默认不渲染,替换为 <!--raw HTML omitted --> 代码块。

1# config/_default/markup.yml
2goldmark:
3  renderer:
4    unsafe: true

attribute

是否解析 {...} 额外语法。默认不解析,直接识别为普通文本。

1# config/_default/markup.yml
2goldmark:
3  parser:
4    attribute:
5      block: true
6      title: true
1# title_01
2{.myclass}
3
4> foo
5> bar
6{.myclass}

extensions

https://github.com/yuin/goldmark/#extensions

Highlight —— 配置"代码高亮"风格

Hugo 使用 chroma 作为代码高亮解析器。

官方文档:

 1# config/_default/markup.yml
 2highlight:
 3  anchorLineNos: false # 为每一行代码标注链接,如:"#hl-3-6"为第三个代码块中第六行
 4  lineAnchors: "" # 链接前缀,默认"hl"
 5  codeFences: true # 解析{...}扩展选项
 6  guessSyntax: false # 猜测语法,关闭加速编译
 7  hl_Lines: ""
 8  hl_inline: false # 行高亮,一般不在这里设置,在codeFence中设置
 9  lineNoStart: 1
10  lineNos: false # 行号
11  lineNumbersInTable: true # 可能有适配问题,旧版本关闭的
12  noClasses: true # 使用class标签,而不是内嵌的内联样式
13  noHl: false
14  style: monokai # 代码高亮主题,参考 https://xyproto.github.io/splash/docs/all.html
15  tabWidth: 4

Table of contents —— 右侧文章大纲配置

简称 “TOC”

1tableOfContents:
2  ordered: false # 是否添加序号,默认false
3  startLevel: 2 # 开始级别,默认1
4  endLevel: 4 # 结束级别,默认3

4 - 布局使用(Layout-usage)🔥

页面使用预定好的布局文件,可以重复应用相同的页面样式。

概念 - 使用布局的好处

在一个站点中,同一类页面(Page)他们的样式一般是一样的。通过将共同的样式代码抽离为一个布局文件(Layout)可以大大避免重复编码,简化编写页面的工作量。同时,也可以统一管理同一种页面的页面布局,使统一站点风格变得可能。

进一步考虑,将页面编写和布局编写的工作分开,可以将责任分割,进一步提高站点的可管理性:

  • 文章编写者专心写文章
  • 样式编写者专心定制站点样式,进一步封装成主题(Theme),可以跨站点使用。

概念 - 页面(Page)和页面布局(Layout)的对应关系

一般情况,我们使用 Hugo 时,会直接使用现成的主题(Themes)。因为主题里面有现成的布局,这样我们就不需要关心如何定制布局,而只需要关注如何使用布局,然后专心内容的编写即可。

当然,知道如何定制布局也非常有用,这将在下一章"布局定制“讨论

了解如何使用布局,首先需要知道页面(Page)和页面布局(Layout)的对应关系。

下图展示了对应关系的几种可能:

  1. 多数情况下,对应关系由 “模板查找顺序(Template lookup order)” 决定。
  2. 少数情况下,对应关系在前置元数据(FrontMatter)中指定。
flowchart LR

  subgraph Page
    p00[content/_index.md]
    p0[content/blog/_index.md]
    p1[content/blog/helloworld.md]
    p2[content/blog/test.md]
    p4["content/news/20231028-happynewyear.md"]
    p6["content/other/xx.md with `layout=blog`"]
  end

  subgraph "Type + Kind"
    s00["home + page"]
    s1["blog + index"]
    s2["blog + page"]
    s3["news + page"]
  end

  subgraph "Layout"
    l00["layouts/home.html"]
    l0["layouts/_default/single.html"]
    l1["layouts/blog/list.html"]
    l2["layouts/blog/single.html"]
    l3["[error] Unfound  &quot;layouts/news/single.html&quot;"]
  end

  p00 --> s00
  p0 --> s1
  p1 & p2 --> s2
  p4 --> s3
  p6 --> s2

  s00 --> l00
  s1 --> l1
  s2 --> l2
  s3 --> l3
  l3 --> l0
  • Kind —— home/page/section
  • Tyoe —— page/section-name(e.g. docs/blog/community)

按模板查找顺序查找页面布局

根据 “模板查找顺序(Template lookup order)” 找到页面和页面布局的对应关系: (细节使用参考官方文档,下面仅为个人"最佳实践”)

  1. Type —— 页面类型,找到模板所在目录。如:content/blog/xx.md的Type默认为blog,于是找有没有定义layouts/blog/,如果没有则找layouts/_default/
  2. Kind —— 页面种类,找到模板
    • home —— 固定位置 content/_index.md 文件使用 layouts/home.html 页面布局
    • section —— 顶部文件夹文件名 _index.md 文件 (如content/blog/_index.md) 使用 layouts/blog/list.html 页面布局
    • page —— 非顶部文件夹文件名 _index.md 文件 (如content/docsy/content-management/_index.md) 或者非 _index.md 文件 (如content/docsy/command.md) 使用 layouts/docs/single.html 页面布局

一般依据上述规则就可以找到页面布局存放的位置。如果位置上没有页面布局文件,则找最近的 baseof.html 文件。

在前置元数据(FrontMatter)中指定页面布局

type 或者 layout 中指定 section,必要的时候用 kind 指定页面类型。

e.g. 下面的前置元数据将指定使用 layouts/blog/single.html 模板

1---
2kind: page
3type: blog
4---

相关视频教程

(从左往右看)

5 - 布局定制(Layout-custom)📦

通过模板(Templates)技术,预先定义好的页面样式。

官方文档:

模板引擎

布局使用: 参考"布局使用"

Hugo 使用 Go 中 html/template 库 作为模板引擎。 通过模板引擎,可以解析模板中的 {{...}} 标签。 标签 {{...}} 在 Go 中称为 “Action” (动作)。 动作包括两种类型:数据求值和控制结构。

  • 基础语法

     1//点
     2{{ . }}
     3代表传递给模板的数据,表示当前模板的上下文,可以是 Go 语言中的任何类型,比如字符串、数组、结构体等
     4点的使用参考:https://www.regisphilibert.com/blog/2018/02/hugo-the-scope-the-context-and-the-dot/
     5
     6//注释
     7{{/* comment */}}
     8
     9//空格
    10{{- pipeline -}} // 清除 pipeline 前后的空格
    11{{- pipeline }} // 清除 pipeline 前面的空格
    12
    13//变量赋值
    14{{$变量名 := "值"}}
    15
    16//条件判断
    17{{if pipeline}} T1 {{else}} T0 {{end}}
    18如果不为空则输出T1,否则输出T0
    19{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
    20
    21//循环语句
    22{{range pipeline}} T1 {{end}}
    23pipeline 的值必须是数组,切片,map,channel,设置 点. 为数组,切片遍历 map 的值,输出T1
    24
    25//with 重设点.的值
    26{{with pipeline}} T1 {{else}} T0 {{end}}
    27如果 pipeline 的值为空, 点. 的值不受影响,输出T1,否则 点. 的值设置成 pipeline 的值,输出T0
    
  • 定义子模板

     1//define
     2{{define "name"}} T1 {{end}}
     3定义一个特定名称的模板
     4
     5//template
     6{{template "name"}}
     7引入指定名称的模板,不传入任何数据.
     8
     9{{template "name" pipeline}}
    10引入指定名称的模板,设置模板上下文 点. 的值为 pipeline 的值
    11
    12//block
    13{{block "name" pipeline}} T1 {{end}}
    14定义特定名称的模板,并在当前位置引入该名称的模板,模板的上下文 点. 的值为 pipline 的值,如果该名称的模板未实现(不存在),则输出T1
    

变量/函数/数据

官方文档:

本站示例: 测试模板中函数和变量的取值

视频教程:

逻辑

视频教程:

6 - 内容管理(Content-Management)🔥

记录如何编写 content/<section_name>/**/<content_name>.md 文件。 (涉及东西多,篇幅长)

content 的目录结构

└── content
    ├── _index.md          // [home]            <- https://example.com/ **
    ├── about.md           // [page]            <- https://example.com/about/
    ├── posts               
    |   ├── _index.md      // [section]         <- https://example.com/posts/ **         
    |   ├── firstpost.md   // [page]            <- https://example.com/posts/firstpost/
    |   ├── happy           
    |   |   ├── _index.md  // [section]         <- https://example.com/posts/happy/ **
    |   |   └── ness.md    // [page]            <- https://example.com/posts/happy/ness/
    |   └── secondpost.md  // [page]            <- https://example.com/posts/secondpost/
    └── quote   
        ├── _index.md      // [section]         <- https://example.com/quote/ **           
        ├── first.md       // [page]            <- https://example.com/quote/first/
        └── second.md      // [page]            <- https://example.com/quote/second/

// hugo默认生成的页面, 没有对应的markdown文章
分类列表页面               // [taxonomyTerm]    <- https://example.com/categories/  **
某个分类下的所有文章的列表  // [taxonomy]        <- https://example.com/categories/one-category  **
标签列表页面               // [taxonomyTerm]    <- https://example.com/tags/  **
某个标签下的所有文章的列表  // [taxonomy]        <- https://example.com/tags/one-tag  **

中括号[]中标注的是页面的kind属性, 他们整体上分为两类: single(单页面 - page) 和 list(列表页 - home, section, taxonomyTerm, taxonomy).

6.1 - 前置元数据(Frontmatter)

设置文章的标题、创建日期、描述等。

官方文档:

使用

page

1# content/test/_index.md
2title: "My amazing new section"
3weight: 1
4type: docs
5description: >
6    A special section with a docs layout.    

list/section

1# content/news/_index.md
2title: "Latest News"
3linkTitle: "News"
4menu:
5main:
6    weight: 30
7cascade:
8    - type: "blog"
1# content/sections01/_index.md 
2
3# section 子文章"块"显示
4simple_list: true # 列表显示
5no_list: true # 不显示

链接 linking

  • ref —— 绝对路径
  • relref —— 相对路径
 1# hostname=localhost
 2# port=1313
 3# baseurl=/
 4.
 5└── content
 6    ├── document1.md
 7    ├── about
 8    |   ├── _index.md
 9    |   └── document1.md
10    ├── pages
11    |   ├── current.md <----------- current
12    |   ├── document1.md
13    |   └── document2.md // has anchor #anchor
14    ├── products
15    |   └── index.md
16    └── blog
17        └── my-post.md
18
19e.g. 
20{{< relref "document1.md" >}} --> 可以忽略后缀
21{{< relref "document1" >}} --> 输出: /content/pages/document/
22{{< ref "document1" >}}    --> 输出: //localhost:1313/content/pages/document/
23{{< ref "document2.md#anchor" >}}
24{{< ref "document1" >}}        --> 找到: /content/pages/document1.md
25{{< ref "/document1" >}}       --> 找到: /content/document1.md
26{{< ref "/about/document1" >}} --> 找到: /content/aboug/document1.md

路径不需要绝对"准确",hugo会(按照优先级)自动匹配最适合的结果。

优先级:

  1. ./*
  2. ./**
  3. content/*
  4. content/**

6.2 - 简码(Shortcode)

简码,顾名思义"用于简化操作的代码块"。

我们在文章内容 content/**.md 可以重复多处调用这种代码块来减少重复性操作。

区分: 简码(Shortcode)和模板变量与函数(Template variable and function)

模板的设计初衷是简化操作,避免重复编码。但是如果(表达作者思想的)文章中出现大量(页面渲染和逻辑判断相关的)模板语句会使文章管理变得混乱。

为了避免上述问题,Hugo 提出简码的概念: 文章作者不能直接在文章中使用模板语言,但是可以使用模板语言封装后的"简码(Shortcode)"。简码封装了模板语言涉及的html和逻辑判断。使用简码只需要传入必要参数即可。

  • 模板变量调用: {{ .Title }}
  • 模板函数调用: {{ dict "title" .Title content" "hello!" | jsonify }}
  • 简码调用: {{< highlight go >}} hello {{< /highlight >}}

总结: 模板只能在 layouts/ 中使用;简码只能在 content/ 中使用。这样就划分了两个角色: 编写模板的主题作者和编写文章的内容作者!

官方文档:

调用

有两种调用形式:

  • {{< ... >}} —— 不对传入参数进行处理
  • {{% ... %}} —— 对传入参数进行加工,如进行markdown标志的解析

有三种参数传递形式:

  • {{< shortcodename >}} —— 不传参
  • {{< shortcodename parameters >}} —— 传入 "string" 或者 key="value" 形式的参数 (简码中会通过 .Get 函数获取传入参数)
  • {{< shortcodename >}} inner string {{< /shortcodename >}} —— 用两个简码标记包裹字符串 (简码中会通过 .Inner 变量获取包裹的字符串)

简码"不解析"

有的时候,我们就希望简码字符直接以字符形式显示,像 “{{< string >}}” 这样。这时我们只需要将内容用 /* ... */ 包裹,如 “{{</* string */>}}” 写在 .md 文件中。

1{{</* string */>}} --解析--> {{< string >}} 
2{{%/* string */%}} --解析--> {{% string %}} 

6.3 - 分类法(Taxonomy)

给文章打标签,便于检索。

默认有 tagscategories 两个。添加更多只需要在配置文件配置:

1taxonomies:
2  tag: tags
3  category: categories
4  project: projects

默认会把全部标签显示在右侧,称为 “标签云(taxonomyCloud)"。 可以配置只显示一部分标签:

 1params:
 2  taxonomy:
 3    taxonomyCloud:
 4      - projects    # remove all entries
 5      - tags        # to hide taxonomy clouds
 6    taxonomyCloudTitle:   # if used, must have the same
 7      - Our Projects      # number of entries as taxonomyCloud
 8      - Tag Cloud
 9    taxonomyPageHeader:
10      - tags        # remove all entries
11      - categories  # to hide taxonomy clouds

7 - 多语言支持(Multi-language)

为页面框架、文章添加多国语言支持,也叫"国际化"。

官方文档:

页面框架"国际化"

首先,需要在 i18n/ 目录中定义多语言的显示内容。 (这些内容一般由主题项目提供)

i18n/
├── en.toml (默认)
└── zh-cn.toml <---- 新增

e.g. docsy/i18n/zh-cn.toml

1[ui_pager_prev]
2other = "上一页"
3
4[ui_pager_next]
5other = "下一页"
6
7[ui_read_more]
8other = "更多"

然后,在配置文件中更改 defaultcontentlanguage 配置设置。 (默认en

1defaultcontentlanguage: zh-cn

最后,通过调用模板函数 {{ T "ui_pager_prev" }} 就能得到当前环境的框架文本。

文章内容"国际化"

https://www.docsy.dev/docs/language/
https://before80.github.io/docsy_docs_with_hugo/docs/Multi-languageSupport/

1content/en/

站点检索"国际化"

设置 languagecode 配置有利于浏览器和搜索引擎识别站点语言。

它的作用:

  1. 改变内部 RSS 模板中的 <language> 元素
  2. 改变内部别名模板中 <html> 元素的 lang 属性

取值参考: HTML Language Code Reference