注意:
本文档为对
官方文档
的总结和中文翻译(详尽)。整理该文档供学习和查阅使用
Hugo 默认支持 yaml
toml
json
的配置形式,对 yaml
的支持最好。为方便后面的内容编写,本文全部采用 yaml
配置,其他配置形式自行参阅转换
Hugo 基于 Golang 的 html/template 语法,如果你完全不懂 Go,可以参阅
万字速通 Golang
简介
关于 Hugo
Hugo 安全模型
运行时安全
Hugo 生成静态文件,因此一旦构建,运行时就是浏览器与集成的服务。但在开发和构建时,hugo 是一个可执行文件
Hugo 保证安的主要方法是沙箱和严格的安全策略:
虚拟文件系统,只有主项目(不含第三方)被允许挂载项目之外的目录
只有主项目可以遍历符号链接
用户对文件系统只读
使用一些外部的二进制文件支持 ASCII 和类似的文件。但是这些都是预定义的,默认情况下是禁止的(
查看安全策略
)
安全策略
Hugo 内置安全策略,限制对 os
exec
远程通信
的访问
任何使用不在安全策略允许列表中的功能的构建都将失败,默认配置如下所示 config.yaml
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
security :
enableInlineShortcodes : false
exec :
allow :
- ^dart-sass-embedded$
- ^go$
- ^npx$
- ^postcss$
osEnv :
- (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$
funcs :
getenv :
- ^HUGO_
- ^CI$
http :
methods :
- (?i)GET|POST
urls :
- .*
注:这些和其他配置设置可以被OS环境覆盖
如果要阻止所有远程HTTP获取数据:HUGO_SECURITY_HTTP_URLS=none hugo
依赖安全
Hugo构建为一个静态二进制文件,使用Go模块来管理其依赖关系
建议将此文件提交到版本控制系统。如果校验和不匹配,Hugo构建将失败,这表明存在依赖性篡改。
Web 应用安全
模板和配置作者受信任,但是发送的数据不受信任
内容文件中的端短代码和数据处理是可信的
Hugo 生成的是静态站点,没有用户输入的概念
默认 删除/转义 潜在的不安全内容
通用数据保护规则(GDPR)
General Data Protection Regulation(GDPR),关于如何配置站点以满足新规则:
注意 :
这些设置默认关闭,必须对网站进行自己的评估,并应用适当的设置
适用于内部模板,某些主题可能包含用于嵌入Google Analytics等服务的自定义模板。在这种情况下,这些选项无效
在未来的版本中进一步改进
所有隐私设置
以下是所有隐私设置。这些设置需要放在站点配置中 config.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
privacy :
disqus :
disable : false
googleAnalytics :
disable : false
anonymizeIP : true # 使用户的IP地址在Google Analytics中匿名
respectDoNotTrack : true # 使GA模板尊重“不跟踪”HTTP标头
useSessionStorage : true # 禁用Cookie的使用,并使用会话存储存储GA客户端ID,GA@4 不支持
instagram :
disable : false
simple : true # 构建静态和非JS版本的Instagram图像卡,需要禁用内联样式:services.instagram.disableInlineCSS=true
twitter :
disable : false
enableDNT : true # 推特及其在您网站上的嵌入页面,不会用于包括个性化建议和个性化广告的目的
simple : true # 将生成一个静态的、无JS版本的推文,需要禁用内联样式:services.twitter.disableInlineCSS=true
vimeo :
disable : false
enableDNT : true # vimeo播放器将被阻止跟踪任何会话数据,包括所有cookie和统计数据
simple : true # 视频缩略图将从Vimeo的服务器中获取,并覆盖有播放按钮。如果用户单击播放视频,它将直接在Vimeo网站上的新选项卡中打开
youtube :
disable : true
privacyEnhanced : true # 当打开隐私增强模式时,除非用户播放嵌入视频,否则YouTube不会在您的网站上存储访问者的信息
注:添加注释的默认值都为 false
禁用所有服务
禁用Hugo中所有相关服务的隐私配置示例。使用此配置,其他设置无关紧要
1
2
3
4
5
6
7
8
9
10
11
12
13
privacy :
disqus :
disable : true
googleAnalytics :
disable : true
instagram :
disable : true
twitter :
disable : true
vimeo :
disable : true
youtube :
disable : true
选择 Hugo
Hugo是一个用Go编写的快速、现代的静态网站生成器
使用Hugo构建的网站非常快速和安全:
网站可以托管在任何地方,包括Netlify、Heroku、GoDaddy、DreamHost、GitHub Pages、GitLab Pages、Surge、Firebase、Google Cloud Storage、Amazon S3、Rackspace、Azure和CloudFront
并且可以与CDN协同工作。Hugo站点运行时不需要数据库或依赖昂贵的运行时
为什么速度块
从技术上讲,Hugo 使用文件和模板的源目录,并将其作为输入创建一个完整的网站
适用人群
喜欢在文本编辑器而不是浏览器中写作的人:
为那些希望手工编写自己的网站而不必担心设置复杂的运行时、依赖关系和数据库的人而设计
适用于创建博客、公司网站、投资组合网站、文档、单个登录页面或具有数千页的网站的用户
Hugo 特点
Hugo 拥有极快的速度、强大的内容管理和强大的模板语言,使其非常适合各种静态网站
注:当页面内容和页面数量较多时,基于 go 的 Hugo 速度具有明显优势
网站特点
极快的构建时间(每页<1毫秒)
完全跨平台,易于在macOS、Linux、Windows等平台上安装
在开发过程中使用 LiveReload 实时渲染更改
强大的主题
在任何地方托管您的网站
配置特点
项目的直接组织,包括网站部分
可自定义 URLs
支持可配置分类法,包括类别和标记
通过
强大的模板函数
按需对内容进行
排序
自动生成目录
动态菜单创建
Pretty URLs 支持
Permalink 模式支持
通过别名重定向
TODO:记得回来添加锚点
内容特点
本机Markdown和Emacs Org Mode支持,以及通过外部助手提供的其他语言(请参阅支持的格式)
OML、YAML和JSON元数据支持
可自定义主页
多种内容类型
自动定义的用户和内容摘要
短代码丰富 Markdown 内容
阅读分钟数
单词数量
其他特点
集成 Disqus 评论支持
集成的 Google Analytics 支持
自动创建 RSS
支持 Go HTML 模板
语法高亮显示,由 Chroma 提供
静态站点生成的优点
性能卓越
资源占用低
安全性高
利于 SEO
安装
前提条件
尽管并非在所有情况下都需要,但使用 Hugo 同步文档时经常使用
Git
和
Go
版本
Hugo有两个版本:标准版和扩展版。使用扩展版,您可以:
建议安装扩展版
源码安装
源码编译安装,首先安装好依赖的工具:
设置好 GOPATH
环境变量,获取源码并编译:
1
2
$ export GOPATH = $HOME /go
$ go get -v github.com/spf13/hugo
源码会下载到 $GOPATH/src
目录,二进制在 $GOPATH/bin/
如果需要更新所有Hugo的依赖库,增加 -u
参数:
1
$ go get -u -v github.com/spf13/hugo
二进制安装
到
Hugo Releases
下载对应的操作系统版本的Hugo二进制文件(hugo
或者 hugo.exe
)
Mac下直接使用 Homebrew
安装:
注:尽量选择带 extended
的扩展版,除非你现在和将来使用的主题都不带 Sass
开始
命令
使用 hugo
命令生成站点,如果是 windows 系统:
不要使用 Command Prompt 和 Windows PowerShell
使用 PowerShell(与 Windows PowerShell 不一样)
创建站点
打开命令行工具,选择博客代码需要置放的目录
1
2
3
4
5
6
7
$ hugo new site quickstart # 新建 quickstart 目录
$ cd quickstart
$ git init # 初始化仓库,准备添加主题
$ git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke
$ echo "theme = 'ananke'" >> config.toml # 配置文件配置主题(为主题目录名)
$ hugo # 生成静态页面到 public 文件夹下(不会删除上一次生成的,有需要可以先手动删除)
$ hugo server # 启动本地服务,预览站点
创建页面
1
$ hugo new posts/first.md # 在 quickstart/content/posts/ 下创建页面 first.md
默认生成页面头部注释,配置页面元数据,用于站点预览、分类、聚合等
1
2
3
4
5
---
title : 第一篇文章
date : 2022-11-20T09:03:20 -08 : 00
draft : true # 草稿
---
注:还有封面等其他元数据配置,前往
页面头部注释
追加一些 markdown
内容在 first.md
注释之后
启动本地测试服务,会监听文件内容更新,liveLoaded
1
2
3
4
5
# 由于新建的文章注释中:draft=true,hugo server 命令默认不会生成草稿页面
# 如果需要生成,执行下面的命令
$ hugo server --buildDrafts
# 或者
$ hugo server -D
配置站点
如果你进行了之前的操作,config.yaml
文件将会是:
1
2
3
4
5
baseURL : "http://example.org/"
languageCode : "en-us"
title : "My New Hugo Site"
theme : "ananke"
# 可以自行更改配置
其他命令
1
2
3
4
5
6
7
$ hugo version
$ hugo help
$ hugo server --help
$ hugo --buildDrafts # or -D。同时生成 draft=true 的页面
$ hugo --buildExpired # or -E。同时生成 expiryDate 时间小于当前时间的页面
$ hugo --buildFuture # or -F。同时生成 date 或 publishDate 时间大于当前时间的页面
$ hugo server --navigateToChanged # 启动服务,且路由跳转到正在更新的页面
配置
站点目录
1
2
3
4
5
6
7
8
9
10
11
12
quickstart/
├── archetypes/
│ └── default.md # 指明新生成的页面默认的头部注释
├── assets/ # 存储所有需要由 Hugo Pipes 处理的文件。只有使用了 .Permalink 或 .RelPermalink 的文件才会发布到public 目录
├── config/ # 默认情况下不会创建。存储 yaml、toml、json 文件,几乎不用配置,但是 Hugo 提供了配置指令
├── content/ # 网站的所有内容都将位于此目录中。content 下的顶级文件夹表示分类
├── data/ # 存储Hugo在生成网站时可以使用的配置文件。也可以创建从动态内容中提取的数据模板
├── layouts/ # 以 .html 文件的形式存储模板,指定如何将内容视图呈现到静态网站中
├── public/ # hugo 命令生成的静态页面存储位置
├── static/ # 存储所有静态内容:图像、CSS、JavaScript等
├── themes/ # 主题目录
└── config.toml # 生成页面配置文件
配置目录
除了使用单个站点配置文件外,还可以使用 configDir 目录(默认为 config/
)来更简单的组织和维护,环境特定设置
每个目录包含一组文件,其中包含环境特有的设置。文件可以本地化为特定语言
1
2
3
4
5
6
7
8
9
10
11
12
13
├── config
│ ├── _default # 默认,会合并目录下的每一个配置
│ │ ├── config.toml
│ │ ├── languages.toml
│ │ ├── menus.en.toml
│ │ ├── menus.zh.toml
│ │ └── params.toml
│ ├── production
│ │ ├── config.toml
│ │ └── params.toml
│ └── staging
│ ├── config.toml
│ └── params.toml
注意:
运行 hugo server
,Hugo 设置 Environment=development,该服务使用 _default 文件夹中的配置文件
运行 hugo
,Hugo 设置 Environment=production,合并 _default + production
staging
下的配置在两种环境下都会合并
重写默认配置文件
1
2
$ hugo --config debugconfig.toml
$ hugo --config a.toml,b.toml,c.toml
Markdown 标记配置
以下是Hugo中所有与标记相关的配置及其默认设置:
Goldmark
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
markup :
goldmark :
extensions : # markdown 扩展标记
definitionList : true # 页面定义列表,换行以 [:+空格] 开头,编辑定义文本
footnote : true # 脚注,文本中的标记将变成上标数字;将放在文档末尾脚注列表中的脚注定义。Text[^1]
linkify : true # 链接
linkifyProtocol : https
strikethrough : true # 删除线。~~~text~~~
table : true # 表格
taskList : true # 有序和无序列表
typographer : true # `text` 或 ``text`` 标记一段文本
parser :
attribute : # 启用自定义属性
block : false # 块级元素解析。引用、列表、代码块添加 HTML 属性
title : true # 标题解析。### 标题3
autoHeadingID : true # 自动添加 h1-h6 id 属性
autoHeadingIDType : github # github 的标题 id 策略
wrapStandAloneImageWithinParagraph : true # 图片独占一行
renderer :
hardWraps : false # Goldmark忽略段落中的换行符。设置为 true 将换行呈现为 <br> 元素
unsafe : false # Goldmark不会呈现原始HTML和潜在的危险链接。如果有很多内联 HTML 或 JavaScript,开启
xhtml : false #
注:更多细节参考
Goldmark
Highlight
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
markup :
highlight :
anchorLineNos : false
codeFences : true
guessSyntax : false
hl_Lines : ""
hl_inline : false
lineAnchors : ""
lineNoStart : 1
lineNos : false
lineNumbersInTable : true
noClasses : true
noHl : false
style : monokai
tabWidth : 4
注:参考
高亮语法
Table Of Content
1
2
3
4
5
markup :
tableOfContents :
endLevel : 3
ordered : false # 是否生成有序列表而不是无序列表
startLevel : 2
注:只在
Goldmark
渲染器生效
Markdown Render Hooks
查看
Markdown Render Hooks
合并主题配置
_merge
支持三个值:
none:不合并
shallow:浅合并,添加主题中配置,但在项目未配置的 keys
deep:深合并,主题中的配置添加并覆盖项目中的配置
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
build :
_merge : none
caches :
_merge : none
cascade :
_merge : none
frontmatter :
_merge : none
imaging :
_merge : none
languages :
_merge : none
en :
_merge : none
menus :
_merge : shallow
params :
_merge : deep
markup :
_merge : none
mediatypes :
_merge : shallow
menus :
_merge : shallow
minify :
_merge : none
module :
_merge : none
outputformats :
_merge : shallow
params :
_merge : deep
permalinks :
_merge : none
privacy :
_merge : none
related :
_merge : none
security :
_merge : none
sitemap :
_merge : none
taxonomies :
_merge : none
注:不需要像上面这样这么冗长的配置,默认继承更高级别的 _merge
配置字典
这些配置也可在使用 hugo
hugo server
命令时,通过 --configName
指定覆盖
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
# 有值的都是默认值
baseURL : "http://localhost:1313/" # 服务器地址,必须协议开头,/ 结尾
title : "" # 站点标题
dataDir : "data"
assetDir : "assets"
themesDir : "themes"
publishDir : "public"
contentDir : "content"
archetypeDir : "archetypes"
defaultContentLanguage : "en"
paginatePath : "page" # 分页路由
refLinksErrorLevel : "ERROR" # 程序错误等级,任何错误都会退出。另一个值为 WARNING
refLinksNotFoundURL : "" # 未找到页面的路由重定向
titleCaseStyle : "AP" # 每个标题单词大写开头
timeout : "30s" # 超时停止页面生成,避免递归死循环
paginate : 10 # 分页数量
summaryLength : 70 # 页面宽度
rssLimit : -1
pluralizeListTitles : true # 如果没有_index.md,页面列表标题为所在目录的目录名:首字母大写 + 复数
watch : false # 不监听文件变化,开发模式默认 true,生成模式默认 false
noChmod : false # 不同步页面权限
noTimes : false # 不同步页面修改时间
relativeURLs : false # 使所有 URL 相对于内容根目录
uglyURLs : false # 丑化生成的文件路径,/filename.html => /filename/.index.html
hasCJKLanguage : false # 是否是中日韩语言
buildDrafts : false
buildExpired : false
buildFuture : false
cleanDestinationDir : false # 生成时,删除静态目录中没有源文件的文件
removePathAccents : false # 删除生成路由中的空格
canonifyURLs : false # 将相对 URL 转换为绝对 URL
enableEmoji : false # 允许监视文件系统的更改并根据需要重新创建
enableGitInfo : false # 允许为每个页面启用 .GitInfo 对象(如果Hugo站点由Git版本控制)
enableRobotsTXT : false # 允许生成 robots.txt 文件
enableInlineShortcodes : false # 允许行内短代码
enableMissingTranslationPlaceholders : false # 如果缺少翻译,允许显示占位符而不是默认值或空字符串
disableAliases : false # 将禁用别名重定向的生成
disableLiveReload : false # 禁止开发热更新
disablePathToLower : false # url 转小写
disableHugoGeneratorInject : false # 禁止在主页的 HTML 头中注入生成器元标记
defaultContentLanguageInSubdir : false # 在 subdir 中渲染默认内容语言,如 content/en/,/ => /en/
disableKinds: [] # 禁用指定种类的文件生成。此列表中允许的值 : "page" , "home" , "section" , "taxonomy" , "term" , "RSS" , "sitemap" , "robotsTXT" , "404"
disableLanguages : [] # 禁用的语言
copyright : "" # 网站的版权声明,通常显示在页脚中
googleAnalytics : "" # Google Analytics tracking ID
languageCode : "" # 语言代码
newContentEditor : "" # 创建新内容时要使用的编辑器
timeZone : # 设置时区,一般不用设置,取决于系统时间
theme : # 使用的主题列表
languages : # 国际化配置
cascade : # 默认传递的 map
imaging : # 配置图片处理规则
markup : # 标记语言配置
menus : # 菜单配置
minify : # 打包配置
module : # 模块配置
mediaTypes : # 设置页面需要使用的媒体类型
outputFormats : # 媒体数据输出格式配置
params : # 全局配置参数
permalinks : # 动态路由配置
frontmatter : # 默认的头部注释配置
sectionPagesMenu : # 设置为 "main" 时,配置所有页面在都在侧栏展示
outputs : # 页面输出格式
page :
home :
section :
taxonomy :
term :
taxonomies : # 在整个站点中使用自定义分类之前,必须在站点配置中定义默认分类以外的分类
category : categories
series : series
tag : tags
sitemap : # 在站点配置中设置更改频率和优先级的默认值,以及生成的文件的名称
changefreq : monthly # 页面可能更改的频率。有效值:always/hourly/daily/weekly/monthly/yearly/never
filename : sitemap.xml
priority : -1 # 页面相对于站点上任何其他页面的优先级。有效值的范围从 0.0 到 1.0,默认为 -1,忽略
related : # 查询关联性配置,如果未配置,默认为下面的属性
includeNewer : false # 在相关内容列表中包含比当前页面更新的页面
indices : # 查询权重优先级
- name : keywords
weight : 100
- name : date
weight : 10
threshold : 80 # 0-100之间的值。较低的值将提供更多的匹配,但可能不那么相关
toLower : false # 查询都转换为小写匹配,轻微的性能损失以获得更准确的结果
security : # 内置安全策略,一般不需要更改
enableInlineShortcodes : false
exec :
allow :
- ^dart-sass-embedded$
- ^go$
- ^npx$
- ^postcss$
osEnv :
- (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$
funcs :
getenv :
- ^HUGO_
- ^CI$
http :
methods :
- (?i)GET|POST
urls :
- .*
caches : # 配置各种资源缓存的目录及存在时间
assets :
dir : : resourceDir/_gen # 存储此缓存文件的绝对路径。允许的起始占位符是 :cacheDir 和 :resourceDir
maxAge : -1 # 持续时间,-1表示永远,0有效地关闭该缓存,其他时间如:"10s"、"10m"、"10h" 等
getcsv :
dir : : cacheDir/:project
maxAge : -1
getjson :
dir : : cacheDir/:project
maxAge : -1
getresource :
dir : : cacheDir/:project
maxAge : -1
images :
dir : : resourceDir/_gen
maxAge : -1
modules :
dir : : cacheDir/modules
maxAge : -1
build :
# 关闭将j sconfig.json 写入 /assets文 件夹并映射运行 js.Build 的导入,用于 VS Code 的 JS 智能导航
noJSConfigInAssets : false
# 何时将 /resources/_gen 中的缓存资源用于 PostCSS 和 ToCSS,支持 fallback、always、never
useResourceCacheWhen : "fallback"
# 启用后,名为hugo_stats.json的文件将被写入项目根目录
# 其中包含一些关于构建的聚合数据,例如发布的 HTML 实体列表,用于进行 CSS 修剪
writeStats : false
模块
hugo modules
是 Hugo 的核心构建模块:
初始化的站点项目就是一个模块
static
content
layouts
data
assets
i18n
archetypes
都是一个小模块
可以任意组合模块,形成大的虚拟联合文件系统
注:当使用 hugo mod
生成你的站点(启用 go modules 模式),需要用到这些配置,如无需要可跳过
配置模块
顶级模块配置
1
2
3
4
5
6
7
8
module :
noProxy : none
proxy : direct # 定义用于下载远程模块的代理服务器
noVendor : "" # 一个可选的 Glob 模式匹配模块路径
vendorClosest : false # 选择就近的 vendored 模块
private: '*.*' # 专用的路径 : 逗号分隔的glob列表匹配
replacements : "" # 需替换的,模块路径到目录映射/切片列表(逗号分隔),例如: github.com/bep/my-theme->./
workspace : "off" # 要使用的工作区文件。这将启用 Go 工作区模式
hugoVersion
1
2
3
4
5
module :
hugoVersion :
extended : false # 是否需要 Hugo 的扩展版本
max : "" # 最高版本
min : "" # 最低版本
imports
1
2
3
4
5
6
7
8
9
10
module :
imports :
- disable : false # 如果启用,在 go.* 文件中保留任何版本信息时禁用模块
ignoreConfig : false # 如果启用,则不会加载任何模块配置文件,例如config.toml
ignoreImports : false # 如果启用,将不遵循模块导入
noMounts : true # 不要在此导入中装入任何文件夹
noVendor : true # 从不提供此导入(仅在主项目中允许)
# 可以是有效的Go Module模块路径,也可以是存储在主题文件夹中的模块目录名
path : github.com/gohugoio/hugoTestModules1_linux/modh1_2_1v
- path : my-shortcodes
mounts
注意:
添加 mounts 时,将忽略相关目标根目录的默认 mounts
不应该同时拥有这两个配置:contentDir 、staticDir
如果你添加了一个 mounts 部分,你应该删除旧的 contentDir 、staticDir 等设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module :
mounts :
- source : content
target : content
- source : static
target : static
- source : layouts
target : layouts
- source : data
target : data
- source : assets
target : assets
- source : i18n
target : i18n
- source : archetypes
target : archetypes
使用模块
初始化模块
1
$ hugo mod init your_project_path
添加主题
1
2
3
module :
imports :
- path : github.com/spf13/hyde
更新模块
1
2
3
$ hugo mod get -u # 更新所有
$ hugo mod get -u github.com/gohugoio/myShortcodes ... # 递归更新
$ hugo mod get github.com/gohugoio/[email protected] # 更新指定版本
查看依赖图
主题组件
1
2
3
4
5
6
# 可以嵌套使用主题组件
# 项目 -> 短代码 -> 基本主题 -> hyde 主题
theme :
- my-shortcodes
- base-theme
- hyde
内容管理
内容组织
1
2
3
4
5
6
7
8
9
10
11
└── 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/
注:其中的 https://example.com/
就是 config.toml
中配置的 baseURL
路由解析
索引页:_index.md
_index.md
在 Hugo 中扮演着特殊的角色,将主题和内容添加到列表模板中
1
2
3
4
5
6
7
url ( "/posts/my-first-hugo-post/" )
⊢------------^----------⊣
baseurl section slug
⊢--------^--------⊣⊢-^--⊣⊢-------^---------⊣
permalink
⊢--------------------^---------------------⊣
https://example.com/posts/my-first-hugo-post/index.html
baseurl
:config.toml
中配置的 baseUrl
section
:项目目录位置指定,如 /posts
表示在 content/posts
目录下
slug
:默认为文件名,当元数据注释中的 slug
指定,将会替换
重定义规则
指定页面路由
1
2
3
4
5
---
title : A new post with the filename old-post.md
slug : "new-post"
---
# 将会生成 https://example.com/posts/new-post/
指定模板
1
2
3
4
5
6
---
title : My Post
type : new
layout : mylayout
---
# 将会使用 layouts/new/mylayout.html 渲染该页面
指定页面 URL
1
2
3
4
5
---
title : Old URL
url : /blog/new-url/
---
# 将会生成 https://example.com/blog/new-url/
目录解析
一个页面支持两种目录解析
叶子页面
集合页面
用法
单个页面的内容和附件集合
章节页面附件集合(主页、章节、分类术语、分类列表)
索引文件名
index.md
_index.md
允许的资源
页面和非页面(如图像、PDF 等)
仅限非页面(如图像、PDF 等)
资源位置
当前目录
_index.md
所在顶级目录
布局类型
single
list
嵌套
不允许嵌套其他页面
允许在其下嵌套集合和页面
例
content/posts/test/index.md
content/posts/_index.md
非索引页面文件
仅作为页面资源访问
仅作为常规页面访问
叶子页面
目录示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 目录示例
content/
├── about # 叶子页面
│ ├── index.md
├── posts # 分类叶子页面
│ ├── post1
│ │ ├── content1.md
│ │ ├── content2.md
│ │ ├── image1.jpg
│ │ ├── image2.png
│ │ └── index.md
│ └── post2
│ └── index.md
│
└── another-category # 集合页面
├── ..
└── collection # 集合中的集合
├── ..
└── another-post
└── index.md
无头捆绑
无头捆绑是配置为不在任何地方发布,指的是含头部注释 headless=true
的 .md
文件
不会生成 HTML 在 public
目录
不作为页面部分,如 .Site.RegularPages
但是可以通过 go模板语法
,获取到这部分内容,例如:
1
2
3
4
5
6
7
{{ $headless := . Site . GetPage "/some-headless-file" }}
{{ $reusablePages := $headless . Resources . Match "author*" }}
< h2 > Authors </ h2 >
{{ range $reusablePages }}
< h3 >{{ . Title }}</ h3 >
{{ . Content }}
{{ end }}
这种文件使用场景:
共享媒体库
重复使用内容
集合页面
集合页面目录至少包含一个 _index.md
文件
可以将任何文件类型用作内容资源,只要它是可识别的内容类型
目录示例
1
2
3
4
5
6
7
8
9
10
11
content/
├── collection-1
│ ├── content1.md
│ ├── content2.md
│ ├── image1.jpg
│ ├── image2.png
│ └── _index.md
└── collection-2
├── _index.md
└── a-post
└── index.md
内容解析
可以将任何文件放在 /content
目录下,但是会根据文件头部注释和文件扩展名决定是否处理,例如:
Markdown 转 HTML
执行短代码
应用布局
可被解析的内容列表
扩展名
描述
Goldmark
md, markdown, goldmark
可以将默认处理程序设置为其他内容,请参阅
配置标记
Emacs Org-Mode
org
参阅
go-org
AsciiDoc
asciidocext, adoc, ad
需安装
Asciidoctor
RST
rst
需要安装
RST
Pandoc
pandoc, pdc
需要安装
Pandoc
HTML
html, htm
视为内容文件,具有布局,短代码等,它必须具有头部注释。如果没有,它将按原样复制
注:如要处理上述除 Goldmark
和 HTML
的内容,参考
官方文档
图表
Goat
支持
Goat
图表生成,但是 Typora 不支持预览解析,下面的内容通过 Hugo 将会生成 goat
图表:
1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
Mermaid
对于
Mermaid
图表,Typora 支持预览解析,但是 Hugo 不原生支持,下面的内容会生成原始代码,而不是图表:
1
2
3
4
5
6
flowchart LR
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
如果需要,可以自己添加渲染模板:layouts/_default/_markup/render-codeblock-mermaid.html
1
2
3
4
5
6
7
8
9
10
11
< div class = "mermaid" >
{{ - . Inner | safeHTML }}
</ div >
{{ . Page . Store . Set "hasMermaid" true }}
{{ if . Page . Store . Get "hasMermaid" }}
< script src = "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js" ></ script >
< script >
mermaid . initialize ({ startOnLoad : true });
console . log ( "mermaid is initialized" );
</ script >
{{ end }}
然后便可以渲染 mermaid 图表了
flowchart LR
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
头部注释
支持格式
Hugo 通过页面前的头部注释,获取元数据信息,展示预览内容、封面、发布时间、作者等
支持 toml
yaml
和 json
toml:+++
开始和结束
yaml:---
开始和结束。推荐使用
json:一对 {}
之间
1
2
3
4
5
6
7
8
9
10
11
12
title : 页面标题
categories :
- 分类1
- 分类2
date : "2012-04-06"
description : 一些页面描述
slug : 重写页面末尾路由
tags :
- 标签1
- 标签2
- 标签3
- 标签4
注释字典
预定义
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
name : "string:文件名覆盖"
title : "string: 标题"
description : "string: 紧跟标题的描述,预览页面会显示"
summary : "slice: 页面摘要使用的文本"
linkTitle : "string: 创建指向内容的链接"
aliases : "slice: 在输出目录结构中创建的一个或多个别名(重命名旧路径)"
audio : "slice: 音频文件路径"
cascade : "map: 传递指定头部注释"
date : "string: 分配给此页面的日期时间"
lastmod : "string: 上次修改内容的日期时间"
publishDate : "string: 如果在将来,除非配置 --buildFuturehugo,否则不显示"
expiryDate : "string: 如果在过去,除非配置 --buildExpired,否则不显示"
draft : "bool: 是否是草稿,如果是,除非配置 --buildDrafts,否则不显示"
headless : "bool: 不展示的页面,可用与公共资源和页面公共内容"
images : "slice: 与页面相关的图像的路径数组"
isCJKLanguage : "bool: 明确将内容视为中日韩语言"
keywords : "slice: 元数据关键字,可用于 SEO"
layout : "选择布局模板。如果指定,将在布局目录中查找与内容部分对应的同名布局"
markup : "rst/md: 实验功能,指定文档类型,默认是 md"
outputs : "slice: 指定特定于内容的输出格式"
resources : "string: 用于配置页面捆绑包资源"
series : "slice: 此页面所属的系列数组,作为分类的子集"
slug : "string: 输出 URL 的尾部,未指定时为文件名,如果是 index 则为所在目录名"
type : "string: 内容类型;如果未在前言中指定,值为所在目录名"
url : "string: 从 Web 根目录到内容的完整路径"
videos : "slice: 与页面相关的视频的一系列路径"
weight : "int: 排序,值越大优先级越高"
tags : "slice: 页面标签"
categories : "slice: 页面分类"
注:为方便描述,以上字段的支持的值和类型都在字符串描述中。slice
表示字符串数组
用户定义
可以任意添加字段,这些用户定义的键值被放置在一个 .Params
变量中,以便在模板中使用
可以分别通过 .Params.include_toc
和 .Params.show_comments
访问以下字段:
1
2
include_toc : true
show_comments : false
头部注释传递
通过自定义变量 cascade
可以向下传递给后面的页面
可以通过 _target
控制传递的内容和目标
1
2
3
4
5
6
7
8
9
10
11
cascade :
- _target :
kind : "page" # 匹配类型为 page
lang : "en" # 匹配语言为 en
path : "/blog/**" # /blog/ 目录下的所有
environment : "development" # 匹配开发模式
background : "yosemite.jpg"
- _target :
kind : "section"
background : "goldenbridge.jpg"
title : "Blog"
说明:
打包选项
定义 Hugo 在构建网站时,必须如何处理给定的页面
它们存储在名为 _build
的保留头部注释对象中,默认值如下:
1
2
3
4
5
6
7
8
9
10
11
_build :
# always: 包含着所有集合页面中,site.RegularPages,$page.Pages 可获取
# never: 不在任何一个集合页面中
# local: 包含在本地集合中,$page.RegularPage/$page.Page/.Page/.RegularPages,无标题可导航页面
list : always
# 发布页面捆绑的相关资源
publishResources : true
# always: 视为已发布的页面,包含其输出文件(index.html等)和 permalink
# never: 不在任何一个集合页面中
# link: 不会生成在磁盘中,但是会生成一个真实的永久链接
render : always
注:任何页面,无论其生成选项如何,.GetPage
方法始终可用
说明用例
引用页面
项目需要一个 Who we are
内容文件,用于头部注释和正文,以供主页使用,但无需其他地方
1
2
3
4
5
# content/who-we-are.md`
title : Who we are
_build :
list : false
render : false
1
2
3
4
5
6
{{ /* layouts/index.html */ }}
< section id = "who-we-are" >
{{ with site . GetPage "who-we-are" }}
{{ . Content }}
{{ end }}
</ section >
推荐列表
网站需要展示推荐内容,而不发布任何内容文件
为了避免在每个推荐上设置构建选项,可以在推荐部分的内容文件上使用级联传递(cascade
)
1
2
3
4
5
6
7
_build :
render : true
cascade :
_build :
list : true
render : false
title : Testimonials
1
2
3
4
5
6
7
8
{{ /* layouts/_defaults/testimonials.html */ }}
< section id = "testimonials" >
{{ range first 5 . Pages }}
< blockquote cite = "{{ .Params.cite }}" >
{{ . Content }}
</ blockquote >
{{ end }}
</ section >
页面资源
页面资源:图像、其他页面、文档等,具有页面相关 URL 和自己的元数据
页面资源只能从页面捆绑包访问
虽然这些目录的根目录中有 index.md
或 _index.md
文件,但是页面资源仅可用于与其绑定的页面
例如:第一个页面是一个页面包,可以访问10个页面资源,包括音频、数据、文档、图像和视频。尽管第二个页面也是一个页面捆绑包,但它没有页面资源,无法直接访问与第一个文章相关的页面资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
content
└── post
├── first-post
│ ├── images
│ │ ├── a.jpg
│ │ ├── b.jpg
│ │ └── c.jpg
│ ├── index.md ( root of page bundle)
│ ├── latest.html
│ ├── manual.json
│ ├── notice.md
│ ├── office.mp3
│ ├── pocket.mp4
│ ├── rating.pdf
│ └── safety.txt
└── second-post
└── index.md ( root of page bundle)
属性
属性名
描述
ResourceType
资源的媒体类型的主要类型。例如,MIME 类型 image/jpeg
的文件具有 ResourceType
图像
Name
默认值为文件名(相对于所属页面)。可以设置在头部注释
Title
默认值为文件名(相对于所属页面)。可以设置在头部注释
Permalink
绝对 URL 地址
RelPermalink
相对 URL 地址
Content
页面的内容
MediaType
MIME 类型,例如:image/jpeg
MediaType.MainType
MIME 主类型,例如:application/pdf
application
MediaType.SubType
MIME 子类型,例如 PPT 的子类型为:application/pdf
pdf
vnd.mspowerpoint
MediaType.Suffixes
MIME 类型后缀切片
1
2
3
4
5
6
7
8
9
10
11
{{ with . Resources . GetMatch "script.js" }}
< script >{{ . Content | safeJS }}</ script >
{{ end }}
{{ with . Resources . GetMatch "style.css" }}
< style >{{ . Content | safeCSS }}</ style >
{{ end }}
{{ with . Resources . GetMatch "img.png" }}
< img src = "data:{{ .MediaType }};base64,{{ .Content | base64Encode }}" >
{{ end }}
方法
ByType
返回给定类型的页面资源
1
{{ . Resources . ByType "image" }}
Match
返回所有匹配类型的页面资源
1
{{ . Resources . Match "images/*" }}
GetMatch
返回第一个匹配到的类型的页面资源
1
{{ . Resources . GetMatch "images/*" }}
匹配规则
1
2
3
4
5
6
7
8
// Using Match/GetMatch to find this images/sunset.jpg ?
. Resources . Match "images/sun*" ✅
. Resources . Match "**/sunset.jpg" ✅
. Resources . Match "images/*.jpg" ✅
. Resources . Match "**.jpg" ✅
. Resources . Match "*" 🚫
. Resources . Match "sunset.jpg" 🚫
. Resources . Match "*sunset.jpg" 🚫
资源元数据
记录页面使用的绑定资源的信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
resources :
- name : header # .Resources.Name/.Resources.GetMatch "header" 可以获取到
src : images/sunset.jpg
- params :
icon : photo
src : documents/photo_specs.pdf
title : Photo Specifications
- src : documents/guide.pdf
title : Instruction Guide
- src : documents/checklist.pdf
title : Document Checklist
- src : documents/payment.docx
title : Proof of Payment
- name : pdf-file-:counter # pdf-file-1 pdf-file-2 pdf-file-3
params :
icon : pdf
src : '**.pdf'
- params : # 所有 .docx 文档包含 .icon=word
icon : word
src : '**.docx'
:counter
假设有下面的资源定义
1
2
3
4
5
resources :
- src : '*specs.pdf'
title : 'Specification #:counter'
- name : pdf-file-:counter
src : '**.pdf'
解析后
文件名
解析 name
解析 title
checklist.pdf`
pdf-file-1.pdf
checklist.pdf
guide.pdf
pdf-file-2.pdf
guide.pdf
other_specs.pdf
pdf-file-3.pdf
Specification #1
photo_specs.pdf
pdf-file-4.pdf
Specification #2
图像处理
支持缩放、修剪、旋转、滤镜和格式转换
图像资源
要处理图像,必须将图像作为页面资源或全局资源访问
页面资源
页面资源是页面捆绑包中的文件。页面捆绑包是根目录中包含 index.md
或 _index.md
文件的目录
1
2
3
4
5
content/
└── posts/
└── post-1/ <-- page bundle
├── index.md
└── sunset.jpg <-- page resource
全局资源
满足以下条件之一的目录,目录内文件作为全局资源:
通过以下方式访问全局资源
1
2
{{ $image := resources . Get "images/sunset.jpg" }}
{{ $image := resources . GetRemote "https://gohugo.io/img/hugo-logo.png" }}
图像渲染
一旦将图像作为页面资源或全局资源进行了访问
使用 Permalink
、RelPermlink
、Width
和 Height
在模板中进行渲染
例1:如果资源未找到,会抛出错误
1
2
{{ $image := . Resources . GetMatch "sunset.jpg" }}
< img src = "{{ $image.RelPermalink }}" width = "{{ $image.Width }}" height = "{{ $image.Height }}" >
例2:如果资源未找到,跳过渲染(方式1)
1
2
3
4
{{ $image := . Resources . GetMatch "sunset.jpg" }}
{{ with $image }}
< img src = "{{ .RelPermalink }}" width = "{{ .Width }}" height = "{{ .Height }}" >
{{ end }}
例3:如果资源未找到,跳过渲染(方式2)
1
2
3
{{ with . Resources . GetMatch "sunset.jpg" }}
< img src = "{{ .RelPermalink }}" width = "{{ .Width }}" height = "{{ .Height }}" >
{{ end }}
图像处理方法
元数据(Exif、IPTC、XMP等)在图像转换期间不保留
对原始图像使用 Exif
方法从 JPEG 或 TIFF 图像中提取 Exif 元数据
Resize
将图像调整为指定的宽度和/或高度
如果同时指定宽度和高度,则生成的图像将不成比例地缩放,除非原始图像具有相同的纵横比
1
2
3
4
5
6
7
8
{{ /* Resize to a width of 600px and preserve aspect ratio */ }}
{{ $image := $image . Resize "600x" }}
{{ /* Resize to a height of 400px and preserve aspect ratio */ }}
{{ $image := $image . Resize "x400" }}
{{ /* Resize to a width of 600px and a height of 400px */ }}
{{ $image := $image . Resize "600x400" }}
Fit
缩小图像以适应给定的尺寸,同时保持纵横比
1
2
{{ /* 必须同时提供宽度和高度 */ }}
{{ $image := $image . Fit "600x400" }}
Fill
裁剪并调整图像大小以匹配给定尺寸
1
2
{{ /* 必须同时提供宽度和高度,使用定位选项更改裁剪框定位点 */ }}
{{ $image := $image . Fill "600x400" }}
Crop
裁剪图像以匹配给定尺寸,而不调整大小
1
2
{{ /* 必须同时提供宽度和高度,使用定位选项更改裁剪框定位点 */ }}
{{ $image := $image . Crop "600x400" }}
Filter
应用一个或多个滤镜给指定图像
1
{{ $image := $image . Filter ( images . GaussianBlur 6 ) ( images . Pixelate 8 ) }}
使用
pipes
,Hugo 将按顺序应用
1
{{ $image := $image | images . Filter ( images . GaussianBlur 6 ) ( images . Pixelate 8 ) }}
创建滤镜链,然后重用
1
2
3
{{ $filters := slice ( images . GaussianBlur 6 ) ( images . Pixelate 8 ) }}
{{ $image1 := $image1 . Filter $filters }}
{{ $image2 := $image2 . Filter $filters }}
Colors
返回图像主要颜色切片,格式为 hex
,图像越小性能越高
1
{{ $colors := $image . Colors }}
Exif
返回包含图像元数据的 Exif 对象
为了防止在处理没有 Exif 数据的图像时出错,请使用 with
语句包装访问
1
2
3
4
5
6
7
8
{{ with $image . Exif }}
Date : {{ . Date }}
LatLong : {{ . Lat }}, {{ . Long }}
Tags :
{{ range $k , $v := . Tags }}
TAG : {{ $k }} : {{ $v }}
{{ end }}
{{ end }}
或者单独访问 Exif 字段,使用 lang.FormatNumber
函数根据需要设置字段的格式
1
2
3
4
5
6
7
8
9
10
11
12
{{ with $image . Exif }}
< ul >
{{ with . Date }}< li > Date : {{ . Format "January 02, 2006" }}</ li >{{ end }}
{{ with . Tags . ApertureValue }}< li > Aperture : {{ lang . FormatNumber 2 . }}</ li >{{ end }}
{{ with . Tags . BrightnessValue }}< li > Brightness : {{ lang . FormatNumber 2 . }}</ li >{{ end }}
{{ with . Tags . ExposureTime }}< li > Exposure Time : {{ . }}</ li >{{ end }}
{{ with . Tags . FNumber }}< li > F Number : {{ . }}</ li >{{ end }}
{{ with . Tags . FocalLength }}< li > Focal Length : {{ . }}</ li >{{ end }}
{{ with . Tags . ISOSpeedRatings }}< li > ISO Speed Ratings : {{ . }}</ li >{{ end }}
{{ with . Tags . LensModel }}< li > Lens Model : {{ . }}</ li >{{ end }}
</ ul >
{{ end }}
Exif 属性
属性
描述
.Date
图像创建时间/日期,格式化通过
time.Format
函数
.Lat
GPS 纬度
.Long
GPS 经度
.Tags
图像标签集合,可以通过
图像配置
指定包含项或排除项
图像选项
尺寸
所有尺寸都以 px
为单位
旋转
旋转按给定的角度逆时针旋转,假设一个 600x400
的图像需要逆时针旋转 90°
,缩小 50%
:
1
{{ $image = $image . Resize "300x r90" }}
如果不需要缩放,使用 printf
获取原宽高:
1
2
3
4
5
{{ with . Resources . GetMatch "sunset.jpg" }}
{{ with . Resize ( printf "%dx%d r90" . Height . Width ) }}
< img src = "{{ .RelPermalink }}" width = "{{ .Width }}" height = "{{ .Height }}" >
{{ end }}
{{ end }}
锚点
使用
Crop
或
Fill
对图像进行裁剪时,需要指定裁剪的位置,例如将 400x200
从左上角裁剪 200x100
大小:
1
2
3
{{ /* 可选值: TopLeft, Top, TopRight, Left, Center, Right */ }}
{{ /* 可选值: BottomLeft, Bottom, BottomRight, Smart */ }}
{{ $image . Crop "200x100 TopLeft" }}
注:当裁剪时应用了旋转,锚点为旋转后的方向
格式
对图像格式进行转换,例如: .jpg
=> .webp
1
2
3
4
5
6
7
8
{{ /* 可选值: bmp, gif, jpeg, jpg, png, tif, tiff, webp */ }}
{{ $image . Resize "600x webp" }}
// 如果不需要缩放
{{ with . Resources . GetMatch "sunset.jpg" }}
{{ with . Resize ( printf "%dx%d webp" . Width . Height ) }}
< img src = "{{ .RelPermalink }}" width = "{{ .Width }}" height = "{{ .Height }}" >
{{ end }}
{{ end }}
质量
适用于 JPEG
和 WebP
图像,q
值越高,质量越好,取值范围 1-100
。默认值 75
,可以通过
图像配置
1
{{ $image . Resize "600x webp q50" }}
暗示
适用于和 WebP
图像,暗示图像内容,默认值 photo
,可以通过
图像配置
值
描述
drawing
具有高对比度细节的手绘或线条图
icon
彩色小图像
photo
带自然光的室外照片
picture
室内照片,如肖像
text
主要是文本的图像
1
{{ $image . Resize "600x webp picture" }}
背景色
将图像从支持透明度的格式(如PNG)转换为不支持透明度的文件格式(如JPEG)时,可以指定生成图像的背景色
支持 #00f
或 #0000ff
,可以通过
图像配置
1
{{ $image . Resize "600x jpg #b31280" }}
重采样滤镜
可以指定调整图像大小时使用的重采样滤镜,默认值是 Box
,可以通过
图像配置
。常用的重采样滤镜包括:
滤镜
描述
Box
适用于降尺度的简单快速平均滤镜
Lanczos
用于照片图像的高质量重采样滤镜,产生清晰的结果
CatmullRom
锐立方滤镜,比 Lanczos
滤波器更快,同时提供类似的结果
MitchellNetravali
与 CatmullRom
相比,立方滤镜可产生更平滑的结果,且振铃伪影更少
Linear
双线性重采样滤波器,产生平滑输出,比 MitchellNetravali
更快
NearestNeighbor
最快的重采样滤镜,无抗锯齿
1
{{ $image . Resize "600x400 Lanczos" }}
图像处理案例
处理模板:layouts/shortcodes/imgproc.html
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
{{ $img := . Page . Resources . GetMatch ( printf "*%s*" (. Get 0 )) }}
{{ $command := . Get 1 }}
{{ $options := . Get 2 }}
{{ if eq $command "Fit" }}
{{ $img = $img . Fit $options }}
{{ else if eq $command "Resize" }}
{{ $img = $img . Resize $options }}
{{ else if eq $command "Fill" }}
{{ $img = $img . Fill $options }}
{{ else if eq $command "Crop" }}
{{ $img = $img . Crop $options }}
{{ else }}
{{ errorf "无效的图片处理命令,必须是以下之一: Crop, Fit, Fill 或 Resize" }}
{{ end }}
< figure style = "padding: 0.25rem; margin: 2rem 0; background-color: #cccc" >
< img style = "max-width: 100%; width: auto; height: auto;" src = "{{ $img.RelPermalink }}" width = "{{ $img.Width }}" height = "{{ $img.Height }}" >
< figcaption >
< small >
{{ with . Inner }}
{{ . }}
{{ else }}
.{{ $command }} "{{ $options }}"
{{ end }}
</ small >
</ figcaption >
</ figure >
然后在 .md
文件写下面的代码
{{ < imgproc banana Resize “300x” /> }}
说明:
该文档使用 Hugo 搭建,上面代码括号加粗,避免被 Hugo 解析而报错
上面代码使用了自闭合(类似 HTML),如果还有其他短代码,可以嵌套使用
图像配置
1
2
3
4
5
6
7
8
9
10
11
imaging :
anchor : Smart
bgColor : '#ffffff'
hint : photo
quality : 75
resampleFilter : Box
exif :
disableDate : false
disableLatLong : false
excludeFields : ""
includeFields : ""
提示:
Hugo将处理过的图像缓存在resources目录中。如果在源代码管理中包含此目录,Hugo将不必在 CI/CD 工作流中重新生成图像(例如,GitHub Pages、GitLab Pages、Netlify等),这将加快构建速度
如果更改图像处理方法或选项,或者重命名或删除图像,资源目录将包含未使用的图像。要删除未使用的图像,请使用以下命令执行垃圾收集:hugo --gc
短代码
短代码是内容文件中调用内置或自定义模板的简单片段,.html
文件 {{}}
包裹的代码
什么是短代码
Markdown
的内容格式很简单,但有些场景下,需要在 md 中写 HTML 语法
Hugo 创建了短代码来规避在 Markdown 中嵌入 HTML,让 Markdown 保持简洁
短代码是 Markdown 内容文件中的一个简单片段,Hugo将使用预定义的模板呈现该片段
短代码可以随时更新,在站点生成时,很容易合并到更改
提示:
实际上,短代码搭配模板文件使用
如果有文档离开 Hugo 也可阅读的需求,不建议在 Markdown 中使用短代码
如果使用 Hugo 生成网站,即使不在 Markdown 中使用短代码,也需要学习 go 模板文件语法
短代码使用
本文所在博客不准备使用短代码,使用请移步
官网:短代码的使用
内容相关性
Hugo 根据
头部注释参数
来识别页面的相关内容
可以将其调整为所需的索引和参数集,或者使用默认的
内容相关性配置
相关内容列表
假如要列出 5 个相关页面(共享相同的日期或关键字参数)
只需在单个页面模板中包含类似于此部分的内容即可 layouts/partials/related.html
:
1
2
3
4
5
6
7
8
9
{{ $related := . Site . RegularPages . Related . | first 5 }}
{{ with $related }}
< h3 > 相关内容 </ h3 >
< ul >
{{ range . }}
< li >< a href = "{{ .RelPermalink }}" >{{ . Title }}</ a ></ li >
{{ end }}
</ ul >
{{ end }}
.Related
方法接受一个参数,该参数可以是 .Page
或 options
映射。有以下选项:
选项
描述
indices
用于搜索的指数
document
用于搜索相关内容的文档
namedSlice
用于搜索的关键字
fragments
包含一个特殊关键字列表,用于配置为 Fragments
类型的索引
示例:
1
2
3
4
5
6
7
{{ $page := . }}
{{ $opts :=
"indices" ( slice "tags" "keywords" )
"document" $page
"namedSlices" ( slice ( keyVals "tags" "hugo" "rocks" ) ( keyVals "date" $page . Date ))
"fragments" ( slice "heading-1" "heading-2" )
}}
提示:在 Hugo 0.111.0
中改进并简化了这个特性
之前:在此之前,我们有三种不同的方法:Related
、RelatedTo
和 RelatedIndices
之后:只有 Related
内容相关性配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
related :
includeNewer : true
threshold : 60
toLower : false
indices :
- name : tags # 索引名称。此值直接映射到页面参数
type : fragments # basic(默认) 或 fragments
applyFilter : true # 查看后面
cardinalityThreshold : 0 # 0-100,将其设置为 50 将删除索引中 50% 以上文档中使用的所有关键字
weight : 100
pattern : "200601" # 目前仅与日期相关,可能希望列出时间上接近的内容
toLower : false
- name : categories
type : fragments
applyFilter : true
weight : 200
提示:上面的 type
applyFilter
cardinalityThreshold
在 Hugo v0.111.0
以上支持
如果 applyFilter=true
,则结果中每一页上的 .HeadingsFiltered
将反映过滤的标题。如果想显示相关内容列表中的标题,这很有用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{{ $related := . Site . RegularPages . Related . | first 5 }}
{{ with $related }}
< h2 > 相关内容 </ h2 >
< ul >
{{ range $i , $p := . }}
< li >
< a href = "{{ .RelPermalink }}" >{{ . Title }}</ a >
{{ with . HeadingsFiltered }}
< ul >
{{ range . }}
{{ $link := printf "%s#%s" $p . RelPermalink . ID | safeURL }}
< li >
< a href = "{{ $link }}" >{{ . Title }}</ a >
</ li >
{{ end }}
</ ul >
{{ end }}
</ li >
{{ end }}
</ ul >
{{ end }}
页面分区
Hugo 生成一个与内容相匹配的节点树
content/
目录下的一级目录形成一个顶级分区,如果是
叶子页面
,不会形成分区
如果分区内存在目录 Foo
嵌套很深,需要在 _index.md
文件创建 Foo
的目录
嵌套分区
1
2
3
4
5
6
7
8
content
└── blog # <-- 是分区,因为 first-level 目录在 content/ 下
├── funny-cats
│ ├── mypost.md
│ └── kittens # <-- 是分区,因为含有 _index.md
│ └── _index.md
└── tech # <-- 是分区,因为含有 _index.md
└── _index.md
示例:面包屑导航
使用可用的部分变量和方法,您可以构建强大的导航 layouts/partials/breadcrumb.html
:
1
2
3
4
5
6
7
8
9
10
< ol class = "nav navbar-nav" >
< ul >
{{ - range . Ancestors . Reverse }}
< li >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ li >
{{ - end }}
< li class = "active" aria - current = "page" >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
</ li >
</ ul >
</ ol >
页面属性和方法
移步
页面变量
页面列表
Hugo将自动为每个根节创建一个页面,列出该节中的所有内容
有关自定义这些页面呈现方式的详细信息,移步
列表模板
页面分区模板
默认情况下,页面分区中创建的所有内容都将使用与根节名称匹配的内容类型
例如,Hugo 将假设 posts/post-1.md
具有 posts
内容类型。如果页面使用 archetype
,Hugo 将根据archetype/posts.md
中的内容生成主题
页面原型
页面原型是创建新内容时使用的模板,其中包含预配置的主题,也可能是网站内容类型的内容处置。将在运行hugo new
时使用
当运行 hugo new
命令时,会找到项目中最合适的原型模板。如果项目不包含任何原型文件,将在主题中查找,例如:
1
$ hugo new posts/my-first-post.md
将会在下面的目录寻找原型
archetypes/posts.md
archetypes/default.md
themes/my-theme/archetypes/posts.md
themes/my-theme/archetypes/default.md
创建原型模板
archetypes/newsletter.md
1
2
3
4
5
6
7
8
9
10
11
12
13
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---
**Insert Lead paragraph here.**
## New Cool Posts
{{ range first 10 ( where .Site.RegularPages "Type" "cool" ) }}
* {{ .Title }}
{{ end }}
当你创建一个新的页面
1
$ hugo new newsletter/the-latest-cool.stuff.md
将会基于原型模板创建一个新的内容文件
注意:
基于目录的原型
从 Hugo 0.49
开始,可以使用完整的目录作为原型模板。给定此原型目录:
1
2
3
4
5
6
7
archetypes
├── default.md
└── post-bundle
├── bio.md
├── images
│ └── featured.jpg
└── index.md
1
$ hugo new --kind post-bundle posts/my-post
将在 /content/posts/my-post
中创建一个新文件夹,其中包含与 post-bundle
原型文件夹中相同的文件集
所有内容文件(index.md
等)都可以包含模板逻辑,并将接收正确的 .Site
页面分类
Hugo 支持页面内容之间逻辑关系的分类,通过用户自定义
示例:电影网
假设正在制作一个关于电影的网站。可能希望包括以下分类:
Actors
Directors
Studios
Genre
Year
Awards
然后,在每一部电影中,为这些分类法中的每一个指定术语(即,在每部电影内容文件的标题中)
根据这些术语,Hugo 将自动为每个 Actors
、Directors
、Studios
、Genre
、Year
和 Awards
创建页面,每个页面列出与特定 Actors
、Directors
、Studios
、Genre
、Year
和 Awards
相匹配的所有电影
分类配置
默认分类 ,在整个站点中使用自定义分类之前,必须在站点配置中定义默认分类以外的分类:
1
2
3
4
taxonomies :
category : categories
tag : tags
series : series
注:当站点配置 taxonomies
后,上面的默认配置将会失效,使用用户指定的配置
如果不希望 Hugo 创建任何分类
1
2
3
4
5
6
disableKinds :
- taxonomy
- term
- home
- page
- section
在页面头部注释中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
categories :
- Development
categories_weight : 44
project_url : https://github.com/gohugoio/hugo
series :
- Go Web Dev
slug : hugo
tags :
- Development
- Go
- fast
- Blogging
tags_weight : 22
title: 'Hugo : A fast and flexible static site generator'
注意:以上示例中 categories_weight
tags_weight
越大,优先级越高,在同类页面中排序越靠前
页面摘要
Hugo 生成内容摘要,作为摘要视图中的简短版本,.Summary
获取
自动拆分摘要
默认情况下,Hugo 会自动将内容的前 70
个单词作为摘要,并将其存储到 .Summary
页面变量中,以便在模板中使用
可以通过在站点配置中设置 summaryLength
来自定义摘要长度
可以通过使用 plainify
和 safeHTML
函数,自定义 HTML 标记中的摘要如何加载
在中日韩语言的页面,需设置 hasCJKLanguage=true
以准确地计算摘要字数
手动拆分摘要
通过添加 <--more-->
分隔符,划分需要添加到摘要的内容
摘要分隔符之前的内容将用作该内容的摘要,并存储在 .Summary
页面变量中,所有 HTML 格式保持不变
对于
Org 模式的内容
,使用 #more
摘要分割并非 Hugo 独有,在其他文献中,也称为 更多标签
或 摘录分隔符
头部注释摘要
在页面头部注释中使用 summary
配置页面摘要
说明:摘要优先级为 手动拆分摘要
> 头部注释摘要
> 自动拆分摘要
链接引用
通过在短代码中使用 ref
和 relref
创建绝对和相对链接,本文档所在站点不使用,了解移步
官方:链接引用
URL 管理
Hugo 支持永久链接、别名、链接规范化,以及处理相对 URL 和绝对 URL 的多种选项
Permalinks
即绝对 URL
构建网站的默认Hugo目标目录是 public/
,可以通过在站点配置中指定不同的 publishDir
来更改此值
站点配置中的 permalink
选项允许您根据每个节调整目录路径(即URL)
这将更改文件写入的位置,并将更改页面的生成位置,以便模板引用
RelPermalink
将接受由于此选项中的映射而进行的调整
RelPermalink
即相对 URL
动态配置
1
2
permalinks :
posts : /:year/:month/:title/:slug
说明::year
:month
根据页面中的 date
注释的时间替换,:title
和 :slug
分别为 title
和 slug
注释
所有支持的动态配置
值
描述
:title
title
注释
:slug
slug
注释
:section
页面所属分区
:sections[1:last]
页面所属除第一个和最后一个的全部分区
:filename
文件名
:slugorfilename
文件名或者 slug
注释
:year
date
注释时间中的年份(4位数)
:month
date
注释时间中的月份(2位数)
:monthname
date
注释时间中的月份名
:day
date
注释时间中的月份天数(1-2位数)
:weekday
date
注释时间中的周期(0-6)
:weekdayname
date
注释时间中的周期名
:yearday
date
注释时间中的年份天数(1-3位数)
别名
在页面头部注释中配置 aliases
,会创建新的相对和绝对 URL,之前的自动变为旧链接
直接指定
在页面头部注释中配置 url
,直接指定当前页面的相对 URL
Pretty URLs & Ugly URLs
Pretty URLs
:例如 https://example.com/about/
Ugly URLs
:例如 https://example.com/about.html
注:默认为 Pretty URLs
,如果开启 Ugly URLs
,在站点配置中配置 uglyURLs=true
关联 URLs
默认情况下,Hugo 保持所有相对URL不变,当从本地文件系统浏览时,这可能会有问题
在站点配置中设置 relativeURL=true
将导致 Hugo 重写所有相对 URL,使其与当前内容相对
例如 /posts/first/
页面包含链接 /about/
,将会改为 ../../about/
菜单
Hugo 提供简单但功能强大的菜单系统
1
2
3
4
5
6
7
8
menu :
main :
- identifier : about
name : 关于
params :
icon : highlight-menu-item
url : /about/
weight : -110
静态文件
静态文件目录默认在 static/
,可以通过站点配置 staticDir
进行覆盖
static/
目录下的文件会原样复制到生成站点的根目录
目录
目录默认通过标题生成,可以通过配置配置目录标题显示等级,或者自定义模板
目录配置
1
2
3
4
5
markup :
tableOfContents :
endLevel : 5 # 目录显示最低等级
ordered : true # 是否在目录前显示序号
startLevel : 1 # 目录显示最高等级
模板示例:基本目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{{ /* layout/_default/single.html */ }}
{{ define "main" }}
< main >
< article >
< header >
< h1 >{{ . Title }}</ h1 >
</ header >
{{ . Content }}
</ article >
< aside >
{{ . TableOfContents }}
</ aside >
</ main >
{{ end }}
模板示例:部分显示目录
下面的示例中,只有头部注释 toc=true
且文章字数大于 400
才显示目录
1
2
3
4
5
6
7
8
9
{{ /* layouts/partials/toc.html */ }}
{{ if and ( gt . WordCount 400 ) (. Params . toc ) }}
< aside >
< header >
< h2 >{{. Title }}</ h2 >
</ header >
{{. TableOfContents }}
</ aside >
{{ end }}
评论
Hugo 附带了一个内部 Disqus 模板
Hugo 提供了对
Disqus
的支持,Disqus 是一种通过 JavaScript 向网站提供评论和社区功能的第三方服务。
主题可能已经支持 Disqus,但如果不支持,很容易通过
Hugo 内置的 Disqus 部分
添加到模板中
Hugo 提供了将 Disqus 加载到模板中所需的所有代码。在将Disqus添加到网站之前,需要设置一个帐户
1
disqusShortname : yourDisqusShortname
对大多数站点来说,上面的配置已经足够,但是仍然可以在页面头部注释设置以下内容:
disqus_identifier
disqus_title
disqus_url
{{ template “_internal/disqus.html” . }}
提示:请留意 .
替代方案
下面是一些 Disqus 的替代选择
国际化
Hugo支持同时使用多种语言创建网站
可以在站点配置的 languages
部分中定义可用语言
可以在站点配置的 disableLanguages
配置禁用的语言
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
defaultContentLanguage : zh-cn
languages :
zh-cn :
baseURL : https://zh-cn.example.com
languagedirection : 中文
title : 我的博客
weight : 2
en :
baseURL : https://en.example.com
params :
linkedin : https://linkedin.com/whoever
title : My blog
weight : 1
语言块中未定义的任何内容都将返回到该键的全局值
defaultContentLanguage
设置项目的默认语言。如果未设置,默认语言将为 en
如果默认语言需要像其他语言一样在其自己的语言代码(/en
)之下呈现,设置defaultContentLanguageISubdir=true
使用小写语言代码,即使是使用区域语言
可以配置不同语言不同的主机地址,将会在 public
下生成 /zh-cn
和 /en
页面翻译
通过文件名
/content/about.zh-cn.md
/content/about.en.md
注:文件名不含 language code,Hugo 认为是配置的默认语言
通过目录
1
2
3
4
5
6
7
8
9
languages :
en :
contentDir : content/english
languageName : English
weight : 10
zh-cn :
contentDir : content/zh-cn
languageName : 中文
weight : 20
注:可以是项目中任何有效的路径,不一定必须在 content/
下
绕过默认链接
有下面三个页面:
/content/about.en.md
/content/om.zh-cn.md
/content/presentation/propos.fr.md
在三个页面的头部注释都设置 translationKey=about
,它们的链接都将视为已翻译的链接,将有如下特性
不考虑其名称或位置
所有翻译的页面都将共享相同的 URL(除了语言子目录)
页面捆绑资源共享,Markdown、HTML等页面文件除外
如果文件名重复,优先当前目录,然后根据语言设置的优先权重
资源文件同样遵循 language code 文件名,如 banana.en.jpg
参考使用
模板文件使用
创建一个页面已翻译链接列表,模板文件类似下面这样:
1
2
3
4
5
6
7
8
9
10
11
{{ /* layouts/partials/i18nlist.html */ }}
{{ if . IsTranslated }}
< h4 >{{ i18n "translations" }}</ h4 >
< ul >
{{ range . Translations }}
< li >
< a href = "{{ .Permalink }}" >{{ . Lang }} : {{ . Title }}{{ if . IsPage }} ({{ i18n "wordCount" . }}){{ end }}</ a >
</ li >
{{ end }}
</ ul >
{{ end }}
或者列出所有允许的页面链接:
1
2
3
4
5
6
{{ /* layouts/partials/allLanguages.html */ }}
< ul >
{{ range $ . Site . Home . AllTranslations }}
< li >< a href = "{{ .Permalink }}" >{{ . Language . LanguageName }}</ a ></ li >
{{ end }}
</ ul >
注:例子中 i18n "translations"
中,假设当前页面语言为 en
,在语言目录 i18n/en
中配置的 home.other
的值
i18n 函数
假设当前站点语言为 en
,当前页面字数为 100
,预计阅读时长为 1
minutes
国际化目录文件 i18n/en
,含有下面内容:
1
2
3
4
5
6
7
home :
other : Home
wordCount :
other : This article has {{ .WordCount }} words.
readingTime :
one : One minute to read
other : {{.Count}} minutes to read
现在有下面的短代码:
1
2
3
{{ i18n "home" }}
{{ i18n "wordCount" . }}
{{ i18n "readingTime" . ReadingTime }}
返回值分别会是:
1
2
3
Home
This article has 100 words.
one minutes to read # 如果阅读时长为 2: 2 minutes to read
菜单国际化
不使用 i18n
有下面的语言配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
defaultContentLanguage : en
languages :
de :
languageName : Deutsch
menu :
main :
- name : Startseite
url : /
weight : 0
weight : 10
en :
languageName : English
menu :
main :
- name : Home
url : /
weight : 0
weight : 0
模板文件使用:
1
2
3
4
5
6
7
8
< ul >
{{ - $currentPage := . - }}
{{ range . Site . Menus . main - }}
< li class = "{{ if $currentPage.IsMenuCurrent " main " . }} active {{ end }} " >
< a href = "{{ .URL | absLangURL }}" >{{ . Name }}</ a >
</ li >
{{ - end }}
</ ul >
注:将生成一个菜单链接,显示文本为 Home
使用 i18n
有下面的语言配置:
1
2
3
4
5
6
7
defaultContentLanguage : en
menu :
main :
- identifier : about
name : About me
url : about
weight : 1
i18n/en
文件:
1
2
about :
other : About me in i18n
模板文件使用:
1
2
3
4
5
6
7
8
9
10
< ul >
{{ - $currentPage := . - }}
{{ range . Site . Menus . main - }}
< li class = "{{ if $currentPage.IsMenuCurrent " main " . }} active {{ end }} " >
< a href = "{{ .URL | absLangURL }}" >{{ i18n . Identifier | default . Name }}</ a >
{{ /* 或者 */ }}
< a href = "{{ .URL | absLangURL }}" >{{ T . Identifier | default . Name }}</ a >
</ li >
{{ - end }}
</ ul >
注:将生成一个菜单链接,显示文本为 About me in i18n
语法高亮
语法高亮配置
注:使用语法高亮需要在 Markdown 文档使用短代码,这将导致文档离开 Hugo 可读性变差,因此:
本文档所属站点不准备使用,这里不作详细说明
如有需要可参考
官网:语法高亮
模板
模板的作用是告诉 Hugo 如何根据文档(如 .md
)生成特定的 HTML 文件
Hugo 的模板语法分为2种基础模板:
html/templata
text/template
模板语法
基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{{ . }} // 当前上下文
{{ $ . }} // 全局上下文
{{ . Title }} // 获取当前上下文属性 Title
{{ . Params . bar }} // 链式获取
{{ $address }} // 预定义一个变量
{{ add 1 2 }} // 函数调用,相当于 add(1,2)
// 一对 () 表示一段组合表达式,end 表示结束一个条件分支
{{ if or ( isset . Params "alt" ) ( isset . Params "caption" ) }} Caption {{ end }}
// 可以换行
{{ if or
( isset . Params "alt" )
( isset . Params "caption" )
}}
// 多行字符串
{{ $msg := `Line one.
Line two.` }}
变量
1
2
3
4
5
6
7
8
9
10
< title >{{ . Title }}</ title > // 获取头部注释 title 的值,作为 <title> 标签的内容
{{ $address := "123 Main St." }} // 定义变量 $address 并赋值为 "123 Main St.""
{{ $address }} // 使用
{{ $var := "Hugo Page" }}
{{ if . IsHome }}
{{ $var = "Hugo Home" }} // 重新赋值
{{ end }}
Var is {{ $var }}
函数
1
2
{{ add 1 2 }} // 3
{{ lt 1 2 }} // true
说明:上面的例子调用了两个 Hugo 内置函数
add
:返回累加和
lt
:第一个参数是否小于第二个参数,是返回 true
,否返回 false
更多内置函数参阅
内置函数
模板嵌套
当需要嵌套使用一个模板时,需要向其传递它需要访问的数据:
如果传递的是当前上下文,在后面加上点 .
所有模板都在 layouts/
目录下
partial :例如嵌套一个 layouts/partials/header.html
1
{ partial "header.html" . }}
template
在更老的 Hugo 版本中,template
函数用于包含部分模板
。现在它只在调用
内部模板
时有用:
1
{{ template "_internal/opengraph.html" . }}
说明:内部模板一般以 _
开头,且不在 layouts/
下——因为该目录下的都是自定义模板
循环语句
通过 range
可以循环迭代 go 语言的 map
array
slice
示例1 :基本使用
1
2
3
{{ range $ array }}
{{ . }} // . 表示 $array 里面的元素
{{ end }}
示例2 :变量取值
1
2
3
{{ range $elem_val := $array }} // 取出 $array 里的元素并赋值给 $elem_val 变量
{{ $elem_val }}
{{ end }}
示例3 :变量取值、取索引
1
2
3
{{ range $elem_index , $elem_val := $array }}
{{ $elem_index }} -- {{ $elem_val }}
{{ end }}
示例4 :map 遍历
1
2
3
{{ range $elem_key , $elem_val := $map }}
{{ $elem_key }} -- {{ $elem_val }}
{{ end }}
示例5 :空值判断
1
2
3
4
5
{{ range $array }}
{{ . }}
{{ else }}
// 如果为空,进这里
{{ end }}
条件语句
通过 if
else
with
or
and
和 not
提供条件分支逻辑处理
其中 if
with
为一个条件分支的起始,需要 {{ end }}
标记条件分支结束的位置
表达式是以下三种情况视为 false
:
布尔值 false
数值 0
长度为 0 的数组、切片、映射、字符串
示例1 :if
和 with
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 如果设置了头部注释 title,使用 <h4> 标签渲染
{{ if isset . Params "title" }}
< h4 >{{ index . Params "title" }}</ h4 >
{{ end }}
// 同上
{{ with . Params . title }} // 假如 with 换成 if,未设置 title 将会报错
< h4 >{{ . }}</ h4 >
{{ end }}
// 如果设置了头部注释 description,渲染 description。否则渲染 summary
{{ with . Param "description" }}
{{ . }}
{{ else }}
{{ . Summary }}
{{ end }}
示例2 :if
else if
和 else
1
2
3
4
5
6
7
{{ if ( isset . Params "description" ) }}
{{ index . Params "description" }}
{{ else if ( isset . Params "summary" ) }}
{{ index . Params "summary" }}
{{ else }}
{{ . Summary }}
{{ end }}
示例3 :and
和 or
1
2
3
4
{{ if ( and ( or ( isset . Params "title" ) ( isset . Params "caption" )) ( isset . Params "attr" )) }}
// 相当于 js 的
if ( ( params . title || params . caption ) && params . attr )
示例4 :not
1
2
3
{{ if not . Params . notoc }}
< a href = "#" id = "toc-toggle" >{{. Title }}</ a >
{{ end }}
管道
Go模板最强大的组件之一是能够一个接一个地堆叠操作。这是通过使用管道来完成的。概念很简单:
每个管道的输出变成下一个管道的输入
应用场景就是 简化函数嵌套调用 、简化条件判断表达式
由于 Go 模板的语法非常简单,管道对于将函数调用链接在一起至关重要:
限制:只能处理单个值,并且该值将成为下一个管道的最后一个参数
示例1 :简化函数嵌套调用
1
2
3
{{ shuffle ( seq 1 5 ) }}
{{ ( seq 1 5 ) | shuffle }} // 等同于上面的代码
示例2 :简化条件判断表达式
1
2
3
4
5
6
7
8
{{ if or ( or ( isset . Params "title" ) ( isset . Params "caption" )) ( isset . Params "attr" ) }}
Stuff Here
{{ end }}
// 等同于上面的代码
{{ if isset . Params "caption" | or isset . Params "title" | or isset . Params "attr" }}
Stuff Here
{{ end }}
{{- -}}
假设头部注释 title=Hello, World!
,有下面的模板:
1
2
3
4
5
6
7
< div >
{{ . Title }}
</ div >
< div >
{{ - . Title - }}
</ div >
将会输出:
1
2
3
4
5
< div >
Hello, World!
</ div >
< div > Hello, World!</ div >
注释
模板中有两种注释可以使用:
Go 注释
HTML 注释
1
2
Bonsoir , {{ /* {{ add 0 + 2 }} */ }} Eliott .
{{ "<!-- This is an HTML comment -->" | safeHTML }}
注意:Hugo 首先加载的 Go 代码,然后渲染 HTML
如果HTML注释包含了 Go 模板代码,可能导致构建失败
使用参数
可以使用两种参数:
页面参数,这是定义在
头部注释
里的
全局参数,这是定义在
站点配置
的 params
里的
示例1 :页面参数
my_one_page.md
1
2
3
4
5
6
---
title : Roadmap
lastmod : 2017-03-05
date : 2013-11-18
notoc : true
---
my_template.html
1
2
3
4
5
6
7
8
9
10
11
{{ if not . Params . notoc }}
< aside >
< header >
< a href = "#{{.Title | urlize}}" >
< h3 >{{. Title }}</ h3 >
</ a >
</ header >
{{. TableOfContents }}
</ aside >
< a href = "#" id = "toc-toggle" ></ a >
{{ end }}
示例2 :页面参数
config.yaml
1
2
3
4
params :
copyrighthtml : Copyright © 2017 John Doe. All Rights Reserved.
sidebarrecentlimit : 5
twitteruser : spf13
my_template.html
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
{{ /* 渲染 footer */ }}
{{ if . Site . Params . copyrighthtml }}
< footer >
< div class = "text-center" >{{. Site . Params . CopyrightHTML | safeHTML }}</ div >
</ footer >
{{ end }}
{{ /* 渲染 twitter 头像和链接 */ }}
{{ with . Site . Params . twitteruser }}
< div >
< a href = "https://twitter.com/{{.}}" rel = "author" >
< img src = "/images/twitter.png" width = "48" height = "48" title = "Twitter: {{.}}" alt = "Twitter" >
</ a >
</ div >
{{ end }}
{{ /* 渲染站点前五页面链接 */ }}
< nav >
< h1 > Recent Posts </ h1 >
< ul >
{{ - range first . Site . Params . SidebarRecentLimit . Site . Pages - }}
< li >< a href = "{{.RelPermalink}}" >{{. Title }}</ a ></ li >
{{ - end - }}
</ ul >
</ nav >
模板应用规则
一个网站肯定是包含许多不同的页面的,这些不同的页面的 HTML 结构也都不一样
因此,模板文件将会有许多,Hugo 将按照一定的规则,选用特定的模板进行文档的 HTML 生成
Kind
页面类型只有两种:
单页:会选择 layouts/_default/single.html
模板
Home:特殊的单页
集合页:与
页面分区
,
页面分类
有关,会选择 layouts/_default/list.html
模板
Layout
可在页面头部注释 layout
设置,会在 layouts/
找指定的模板
参阅
自定义输出格式
,输出格式由 文件名 和 后缀 组成
Language
假设默认语言是 en
,匹配优先级如下:
index.en.amp.html
index.amp.html
index.en.html
:因为没有指定文件名,所以优先级低于 index.amp.html
Type
页面头部注释 type
指定,如果未设置,值为所在目录名
Section
与
页面分区
,
页面分类
相关
Theme
在 Hugo 中,模板会在项目的 layouts/
和应用主题的 layouts
找到匹配度最高的应用:
如果存在多条匹配度相同的,有下面的优先级:
项目内的 layouts/
优先
主题列表定义在前面的优先
示例
示例很多,但是实际用不上那么多的规则,一般查看所使用的主题的模板,自行修改就好了
这里就不一一列举了,如果需要查阅,下面是官方示例:
自定义输出格式
Hugo 可以有多种格式输出内容,包括 日历事件
、电子书格式
、谷歌AMP
和 JSON搜索索引
,或任何自定义文本格式
本节介绍如何正确配置媒体类型和输出格式的网站,以及在哪里为自定义输出创建模板
媒体类型
媒体类型(MIME)在网络上传输包含两部分标识:文件格式
、格式内容
,即 Type 和 文件后缀:
类型
后缀
application/json
json
application/manifest+json
webmanifest
application/octet-stream
application/pdf
pdf
application/rss+xml
xml
rss
application/toml
toml
application/xml
xml
application/yaml
yaml
yml
font/otf
otf
font/ttf
ttf
image/bmp
bmp
image/gif
gif
image/jpeg
jpg
jpeg
jpe
jif
jfif
image/png
png
image/svg+xml
svg
image/webp
webp
text/calendar
ics
text/css
css
text/csv
csv
text/html
html
text/javascript
js
jsm
mjs
text/jsx
jsx
text/markdown
md
markdown
text/plain
txt
text/tsx
tsx
text/typescript
ts
text/x-sass
sass
text/x-scss
scss
video/3gpp
3gpp
3gp
video/mp4
mp4
video/mpeg
mpg
mpeg
video/ogg
ogv
video/webm
webm
video/x-msvideo
avi
自定义媒体类型
媒体类型支持自定义新增和修改,在
站点配置
中配置:
1
2
3
4
5
6
7
mediaTypes :
text/enriched :
suffixes :
- enr
text/html :
suffixes :
- asp
说明:上面的配置添加了一个新的媒体类型 ext/enriched
,修改了媒体类型 text/html
的后缀为 asp
输出格式定义
下面是所有 Hugo 内置的输出格式定义表:
名称
媒体类型
基本名
rel
纯文本
HTML
丑链接
可链接
HTML
text/html
index
canonical
🚫
✅
✅
✅
AMP
text/html
index
amphtml
🚫
✅
✅
✅
CSS
text/css
styles
stylesheet
✅
🚫
✅
🚫
CSV
text/csv
index
alternate
✅
🚫
✅
🚫
Calendar
text/calendar
index
alternate
✅
🚫
✅
🚫
JSON
application/json
index
alternate
✅
🚫
✅
🚫
MARKDOWN
text/markdown
index
alternate
✅
🚫
✅
🚫
ROBOTS
text/plain
robots
alternate
✅
🚫
✅
🚫
RSS
application/rss+xml
index
alternate
🚫
🚫
🚫
🚫
Sitemap
application/xml
sitemap
sitemap
🚫
🚫
🚫
🚫
WebAppManifest
application/manifest+json
manifest
manifest
✅
🚫
✅
🚫
注意:除上面的定义输出格式外,还有两项:path
和 protocol
只有 AMP
的 path=amp
,其他默认为空
只有 Calendar
的 protocol=webcal://
,其他默认为空
如果需要修改内置的定义格式,可以在
站点配置
中配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
outputFormats :
MyEnrichedFormat :
name : Enriched # 输出格式标识符。这用于定义页面所需的输出格式
baseName : myindex # 基本文件名,默认值:index
isPlainText : true # 使用 Go 的纯文本模板解析器,默认值:false
mediaType : text/enriched # 必须匹配已定义媒体类型的 Type
protocol : bep:// # 替换 http:// 或 https:// 为设置的值
path : enr # 子路径保存输出文件
rel : alternate # 在 link 标签创建 rel 值,默认值:alternate
noUgly : true # 如果站点设置 uglyURLs=true,设置 true 关闭当前格式的 uglyURLs
permalinkable : true # 返回 .Permalink 和 .RelPermalink
isHTML : false # 仅用于 HTML 格式相关情况,默认值:false
weight : 100 # 设置非0值,用于列表排序
# 不需要其他格式,页面的头部注释 outputs 包含当前格式这个配置会忽略,默认值:false
notAlternative : false
页面输出格式
一个页面可以被渲染成多种输出格式
默认输出格式
通过头部注释 kind
配置,下面是映照表:
kind 值
默认输出格式
示例
page
HTML
index.html
home
HTML, RSS
/posts/first.md
=> posts/first/index.html
section
HTML, RSS
/posts/index.md
=> posts/index.html
taxonomy
HTML, RSS
tags
分类 => tags/index.html
term
HTML, RSS
tags=React
的页面 => tags/React/index.html
自定义输出格式
在
站点配置
或
头部注释
中配置:
1
2
3
4
5
6
7
outputs :
home :
- HTML
- AMP
- RSS
page :
- HTML
渲染输出格式
1
2
3
{{ range . AlternativeOutputFormats - }}
< link rel = "{{ .Rel }}" type = "{{ .MediaType.Type }}" href = "{{ .Permalink | safeURL }}" >
{{ end - }}
由于 ``.Permalink和
.RelPermalink只是返回
outputs` 第一个定义的生成地址,对于其他格式的地址,可以通过下面两种方式:
single.json
1
2
3
4
{{ . RelPermalink }} > /that-page/
{{ with . OutputFormats . Get "json" - }}
{{ . RelPermalink }} > /that-page/index.json
{{ - end }}
注意:使用 .RelPermalink
和 .Permalink
需要配置当前格式的 permalinkable=true
1
2
{{ ref "blog/neat.md" "amp" }}
{{ relref "about.md#who" "amp" }}
输出格式模板
参考官网:
不同输出格式的模板应用规则
基础模板
基础模板可以用于定义站点的布局,通过 block
关键字定义一个插槽
可以根据需要在不同的模板中,通过 define
关键字应用不同的 HTML 内容
layouts/_default/baseof.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<! DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" >
< title >{{ block "title" . }}
<!-- Blocks may include default content . -->
{{ . Site . Title }}
{{ end }}</ title >
</ head >
< body >
<!-- Code that all your templates share , like a header -->
{{ block "main" . }}
<!-- The part of the page that begins to differ between templates -->
{{ end }}
{{ block "footer" . }}
<!-- More shared code , perhaps a footer but that can be overridden if need be in -->
{{ end }}
</ body >
</ html >
layouts/_default/list.html
1
2
3
4
5
6
7
8
9
{{ define "main" }}
< h1 > Posts </ h1 >
{{ range . Pages }}
< article >
< h2 >{{ . Title }}</ h2 >
{{ . Content }}
</ article >
{{ end }}
{{ end }}
Render Hooks
渲染钩子允许自定义模板覆盖 markdown 渲染功能
支持的 Hooks:
图片
链接
标题
代码块: v0.93.0
注意: 只有
Goldmark
渲染器支持
Hooks 模板文件
必须为下面的路径和文件名,在项目或者主题目录下
1
2
3
4
5
6
7
8
9
layouts/
└── _default/
└── _markup/
├── render-codeblock-bash.html
├── render-codeblock.html
├── render-heading.html
├── render-image.html
├── render-image.rss.xml
└── render-link.html
渲染链接和图片
在 Hooks 上下文中,链接和图片含有下面的上下文变量:
变量名
描述
.Page
当前页面
.Destination
URL
.Title
HTML title
属性
.Text
HTML innerText
.PlainText
``.Text` 的纯文本内容
示例:渲染链接
假设 markdown
中有这样的一个链接:
1
[Text ](https://www.gohugo.io "Title" )
那么在 layouts/_default/_markup/render-link.html
中可以定义为:
1
< a href = "{{ .Destination | safeURL }}" {{ with .Title }} title = "{{ . }}" {{ end }}{{ if strings.HasPrefix .Destination " http " }} target = "_blank" rel = "noopener" {{ end }}>{{ . Text | safeHTML }}</ a >
说明:上面的代码表示只有 http
开头的链接才会被 Hugo 渲染
示例:渲染图片
1
![Text ](https://gohugo.io/images/hugo-logo-wide.svg "Title" )
1
2
3
4
{{ /* layouts/_default/_markup/render-image.html */ }}
< p class = "md__image" >
< img src = "{{ .Destination | safeURL }}" alt = "{{ .Text }}" {{ with .Title }} title = "{{ . }}" {{ end }} />
</ p >
渲染标题
在 Hooks 上下文中,标题含有下面的上下文变量:
变量名
描述
.Page
当前页面
.Level
标题等级 1-6
.Anchor
HTML 生成的锚点
.Text
HTML innerText
.PlainText
.Text
的纯文本内容
.Attributes
map 类型的标题属性(如 id
class
),link 类型的标题始终为空
.isBlock
v0.108.0
是图像,且markup.goldmark.parser.wrapStandAloneImageWithinParagraph=false
,返回true
.Ordinal
v0.108.0
当前文档中所有图像标题的从零开始的序数
示例
1
2
{{ /* layouts/_default/_markup/render-heading.html */ }}
< h {{ .Level }} id = "{{ .Anchor | safeURL }}" >{{ . Text | safeHTML }} < a href = "#{{ .Anchor | safeURL }}" > ¶ </ a > < /h{{ .Level }}>
将会被渲染为HTML:
1
< h3 id = "section-a" > 标题3< a href = "#标题3" > ¶</ a ></ h3 >
渲染代码块
需要 Hugo @v0.93.0
以上版本,在 Hooks 上下文中,代码块含有下面的上下文变量:
变量名
描述
.Page
当前页面
.Type
代码块使用语言,如 java
.Inner
代码块中的代码
.Options
色度高亮处理选项
.Attributes
从 Markdown 传入的属性
.Position
文件名和位置(行数,列),日志打印中会很有用
.Ordinal
当前文档中所有代码块从零开始的序数
列表模板
列表在Hugo中具有特定的含义和用法,用于呈现站点主页、部分页面、分类列表或标签列表等
列表模板是用于在单个HTML页面中呈现多个内容片段的模板
注:首页的列表模板有专用的一个模板,
列表模板应用规则
可以通过官方提供的视频更详细了解(英文):
VIDEO
默认列表模板
集合页面(Section)列表和分类标签列表默认都是 layouts/_default/list.html
如果应用了主题,如果前面的未找到,还会去主题的 themes/<THEME>/layouts/_default/list.html
下寻找
列表模板应用规则
列表渲染_index.md
有这样的一个内容目录:
1
2
3
4
5
6
7
8
9
10
11
.
...
├── content
| ├── posts
| | ├── _index.md
| | ├── post-01.md
| | └── post-02.md
| └── quote
| | ├── quote-01.md
| | └── quote-02.md
...
其中,content/posts/_index.md
的内容:
1
2
3
4
5
6
7
8
9
---
title: My Go Journey
date: 2017-03-23
publishdate: 2017-03-24
---
I decided to start learning Go in March 2017.
Follow my journey through this new blog.
然后,在 layouts/_default/list.html
模板的内容为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{{ define "main" }}
< main >
< article >
< header >
< h1 >{{. Title }}</ h1 >
</ header >
<!-- "{{.Content}}" pulls from the markdown content of the corresponding _index . md -->
{{. Content }}
</ article >
< ul >
<!-- Ranges through content / posts /* . md -->
{{ range . Pages }}
< li >
< a href = "{{.Permalink}}" >{{. Date . Format "2006-01-02" }} | {{. Title }}</ a >
</ li >
{{ end }}
</ ul >
</ main >
{{ end }}
最终生成站点的 HTML 为 example.com/posts/index.html
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--top of your baseof code-->
< main >
< article >
< header >
< h1 > My Go Journey</ h1 >
</ header >
< p > I decided to start learning Go in March 2017.</ p >
< p > Follow my journey through this new blog.</ p >
</ article >
< ul >
< li >< a href = "/posts/post-01/" > Post 1</ a ></ li >
< li >< a href = "/posts/post-02/" > Post 2</ a ></ li >
</ ul >
</ main >
<!--bottom of your baseof-->
如果不使用_index.md
Hugo 渲染列表 _index.md
不是必须的,例如:首页、集合、分类、标签等页面
当未找到 _index.md
是,列表渲染不含 .Content
并且只有 .Title
等头部注释的元素
例如上面 quote
目录下的两个集合页面,最终渲染为这样的列表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--baseof-->
< main >
< article >
< header >
<!-- 使用的是上下文中的 .Title,因为没有 _index.md 提供 title 注释字段 -->
< h1 > Quotes</ h1 >
</ header >
</ article >
< ul >
< li >< a href = "https://example.com/quote/quotes-01/" > Quote 1</ a ></ li >
< li >< a href = "https://example.com/quote/quotes-02/" > Quote 2</ a ></ li >
</ ul >
</ main >
<!--baseof-->
注意:Quotes
来源是 quote
目录名首字母大写+复数,可以通过站点配置 pluralizeListTitles=false
设置取消
示例
Section
layouts/section/posts.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{{ partial "header.html" . }}
{{ partial "subheader.html" . }}
< main >
< div >
< h1 >{{ . Title }}</ h1 >
< ul >
<!-- Renders the li . html content view for each content / posts /* . md -->
{{ range . Pages }}
{{ . Render "li" }}
{{ end }}
</ ul >
</ div >
</ main >
{{ partial "footer.html" . }}
Taxonomy
分类、标签等分类法的列表,layouts/_default/taxonomy.html
1
2
3
4
5
6
7
8
9
10
11
{{ define "main" }}
< main >
< div >
< h1 >{{ . Title }}</ h1 >
<!-- 遍历与特定分类法术语关联的每个内容文件 , 并呈现 summary . html 内容视图 -->
{{ range . Pages }}
{{ . Render "summary" }}
{{ end }}
</ div >
</ main >
{{ end }}
列表排序
Hugo 中默认的优先级排序是:Weight
> Date
> LinkTitle
> FilePath
layouts/partials/default-order.html
1
2
3
4
5
6
7
8
< ul >
{{ range . Pages }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
weight
layouts/partials/by-weight.html
,weight 越小排序越靠前:
1
2
3
4
5
6
7
8
< ul >
{{ range . Pages . ByWeight }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
date
layouts/partials/by-date.html
1
2
3
4
5
6
7
8
9
< ul >
<!-- orders content according to the "date" field in front matter -->
{{ range . Pages . ByDate }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
publishDate
layouts/partials/by-publish-date.html
1
2
3
4
5
6
7
8
9
< ul >
<!-- orders content according to the "publishdate" field in front matter -->
{{ range . Pages . ByPublishDate }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
expiryDate
layouts/partials/by-expiry-date.html
1
2
3
4
5
6
7
8
< ul >
{{ range . Pages . ByExpiryDate }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
lastmod
layouts/partials/by-last-mod.html
1
2
3
4
5
6
7
8
9
< ul >
<!-- orders content according to the "lastmod" field in front matter -->
{{ range . Pages . ByLastmod }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
length
layouts/partials/by-length.html
1
2
3
4
5
6
7
8
9
< ul >
<!-- 页面长度排序 , 短的在前 -->
{{ range . Pages . ByLength }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
title
layouts/partials/by-title.html
1
2
3
4
5
6
7
8
9
< ul >
<!-- ranges through content in ascending order according to the "title" field set in front matter -->
{{ range . Pages . ByTitle }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
linkTitle
layouts/partials/by-link-title.html
1
2
3
4
5
6
7
8
9
< ul >
<!-- 根据前面内容的 “ linkTitle ” 字段升序排列内容 。 如果没有设置 "linkTitle" 字段 , 则使用 "title" 字段的内容 , 并将该值用于 . LinkTitle -->
{{ range . Pages . ByLinkTitle }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . LinkTitle }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
Parameter
根据指定的参数进行排序,首先找页面头部注释的 <field>
没找到则寻找 .Site.Params.<field>
的默认值
都没找到,将会排在末尾
layouts/partials/by-rating.html
1
2
3
4
<!-- 通过头部注释 "rating" 排序 -->
{{ range (. Pages . ByParam "rating" ) }}
<!-- ... -->
{{ end }}
如果目标字段嵌套在另一个字段下面,则可以使用 .
表示法访问该字段
layouts/partials/by-nested-param.html
1
2
3
{{ range (. Pages . ByParam "author.last_name" ) }}
<!-- ... -->
{{ end }}
Reverse
layouts/partials/by-date-reverse.html
1
2
3
4
5
6
7
8
< ul >
{{ range . Pages . ByDate . Reverse }}
< li >
< h1 >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ h1 >
< time >{{ . Date . Format "Mon, Jan 2, 2006" }}</ time >
</ li >
{{ end }}
</ ul >
列表分组
Hugo 提供内置的函数对站点页面进行分组
Page Field
layouts/partials/by-page-field.html
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 根据页面内容分组 , 在这个例子中 , ".Key" 是集合标题 -->
{{ range . Pages . GroupBy "Section" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . Date . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
注意:在前面的内容中,可以使用 {{ .Title }}
获取 _index.md
的 title
字段
同样可以使用
.GetPage 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- Groups content according to content section . -->
{{ range . Pages . GroupBy "Section" }}
<!-- 找 _index . md 的 "title" 注释 -->
{{ with $ . Site . GetPage "section" . Key }}
< h3 >{{. Title }}</ h3 >
{{ else }}
<!-- 如果没有 _index . md , 使用 ".Key" 的值 , 默认是大写复数的所在目录名 -->
< h3 >{{ . Key | title }}</ h3 >
{{ end }}
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . Date . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
date
layouts/partials/by-page-date.html
1
2
3
4
5
6
7
8
9
10
11
12
<!-- "date" 字段分组 -->
{{ range . Pages . GroupByDate "2006-01" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . Date . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
注:在 v0.97.0+
中,.GroupByDate
参数的时间格式与
.time.Format 函数
相同,并且 .Key
会被国际化
publishDate
layouts/partials/by-page-publish-date.html
1
2
3
4
5
6
7
8
9
10
11
12
<!-- "publishDate" 字段分组 -->
{{ range . Pages . GroupByPublishDate "2006-01" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . PublishDate . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
注:在 v0.97.0+
中,.GroupByPublishDate
参数的时间格式与
.time.Format 函数
相同,并且 .Key
会被国际化
lastmod
layouts/partials/by-page-lastmod.html
1
2
3
4
5
6
7
8
9
10
11
12
<!-- "lastmod" 字段分组 -->
{{ range . Pages . GroupByLastmod "2006-01" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . Lastmod . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
注:在 v0.97.0+
中,.GroupByLastmod
参数的时间格式与
.time.Format 函数
相同,并且 .Key
会被国际化
expiryDate
layouts/partials/by-page-expiry-date.html
1
2
3
4
5
6
7
8
9
10
11
12
<!-- "expiryDate" 字段分组 -->
{{ range . Pages . GroupByExpiryDate "2006-01" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . ExpiryDate . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
注:在 v0.97.0+
中,.GroupByExpiryDate
参数的时间格式与
.time.Format 函数
相同,并且 .Key
会被国际化
Page Parameter
layouts/partials/by-page-param.html
1
2
3
4
5
6
7
8
9
10
11
12
<!-- "param_key" 字段分组 -->
{{ range . Pages . GroupByParam "param_key" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . Date . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
Page Parameter in Date Format
layouts/partials/by-page-param-as-date.html
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 按月和 "param_key" 分组 -->
{{ range . Pages . GroupByParamDate "param_key" "2006-01" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . Date . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
Reverse
分组后,按字母数字顺序(A-Z, 1-100)和日期的反向时间顺序
虽然这些是逻辑默认值,但它们并不总是理想的顺序
有两种不同的语法可以更改 Hugo 的默认顺序:
1.反转方法
1
2
{{ range (. Pages . GroupBy "Section" ). Reverse }}
{{ range (. Pages . GroupByDate "2006-01" ). Reverse }}
2.可选参数
1
2
{{ range . Pages . GroupByDate "2006-01" "asc" }}
{{ range . Pages . GroupBy "Section" "desc" }}
Order Within Groups
由于分组会返回 {{ .Key }}
上下文变量和一个页面切片,上面所有的分组排序都是可用的
下面的例子最终排序为:
每个月一组,
每个组 date
升序
组内标题字母排序
layouts/partials/by-group-by-page.html
1
2
3
4
5
6
7
8
9
10
11
{{ range . Pages . GroupByDate "2006-01" "asc" }}
< h3 >{{ . Key }}</ h3 >
< ul >
{{ range . Pages . ByTitle }}
< li >
< a href = "{{ .Permalink }}" >{{ . Title }}</ a >
< div class = "meta" >{{ . Date . Format "Mon, Jan 2, 2006" }}</ div >
</ li >
{{ end }}
</ ul >
{{ end }}
列表过滤
通过
where 函数
过滤,通过
first 函数
限制数量
首页模板
网站首页通常与其他页面不同,因此,Hugo 为首页单独约定一个唯一模板
在首页模板中,可以使用所有的
站点变量
和
页面变量
官方参考视频(英文):
VIDEO
首页模板应用规则
参考官方文档
编辑首页
Hugo 约定 content/_index.md
为首页,首页类似于
列表模板
layouts/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{{ define "main" }}
< main aria - role = "main" >
< header class = "homepage-header" >
< h1 >{{. Title }}</ h1 >
{{ with . Params . subtitle }}
< span class = "subtitle" >{{.}}</ span >
{{ end }}
</ header >
< div class = "homepage-content" >
<!-- 这里会渲染 content / _index . md 的内容 -->
{{. Content }}
</ div >
< div >
{{ range first 10 . Site . RegularPages }}
{{ . Render "summary" }}
{{ end }}
</ div >
</ main >
{{ end }}
集合页面模板
在集合目录下的 _index.md
编辑头部注释和内容
模板应用顺序参考官方文档
页面类型
页面默认输出格式
示例:默认
layouts/_default/section.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{{ define "main" }}
< main >
{{ . Content }}
< ul class = "contents" >
{{ range . Paginator . Pages }}
< li >{{. Title }}
< div >
{{ partial "summary.html" . }}
</ div >
</ li >
{{ end }}
</ ul >
{{ partial "pagination.html" . }}
</ main >
{{ end }}
示例:.GetPage
通过
.GetPage 函数
定义渲染模板,假设目录关系为:
1
2
3
4
5
6
7
8
9
10
.
└── content
├── blog
│ ├── _index.md # "title: My Hugo Blog" in the front matter
│ ├── post-1.md
│ ├── post-2.md
│ └── post-3.md
└── events #Note there is no _index.md file in "events"
├── event-1.md
└── event-2.md
在模板中使用:
1
< h1 >{{ with . Site . GetPage "section" "blog" }}{{ . Title }}{{ end }}</ h1 >
最终生成的 HTML:
注意:在上面的例子中
如果在模板中获取 events
目录下的集合页面:
1
< h1 >{{ with . Site . GetPage "section" "events" }}{{ . Title }}{{ end }}</ h1 >
最终生成的 HTML:
分类法模板
默认情况下,在页面头部注释中定义 categories
tags
series
会默认生成 HTML 在 /categories
/tags
/series
目录
分类方法
所有方法在 .Site.Taxonomies.<string>
下面
其中 string
是站点配置 taxonomies
中定义的分类名,默认情况下是: categories
tags
series
含有以下方法:
方法
描述
.Get(name)
返回类名为 name
的 WeightedPages
.Count(name)
返回类名为 name
的 WeightedPages 数量
.Alphabetical
返回 OrderedTaxonomy
以字母的顺序
.ByCount
返回 OrderedTaxonomy
以每个类别的数量,大的在前
OrderedTaxonomy
在 Go 中,map 是无序的,因此有序的 OrderedTaxonomy
类型定义为切片:
1
2
3
4
[] struct {
Name string
// ...
}
还有以下方法:
方法
描述
.Term
分类名称
.WeightedPages
weight
排好序的分类集
.Count
分类数量
.Pages
该分类所有页面
.Reverse
顺序反转
WeightedPages
对于 WeightedPages
,是 WeightedPage
的切片,可访问的方法:
方法
描述
.Count(name)
返回类名为 name
的 WeightedPages 数量
.Pages
该分类所有页面
分类顺序
字母顺序
所有分类按照字母排序
1
2
3
4
5
< ul >
{{ range . Data . Terms . Alphabetical }}
< li >< a href = "{{ .Page.Permalink }}" >{{ . Page . Title }}</ a > {{ . Count }}</ li >
{{ end }}
</ ul >
weight优先级
1
2
3
4
5
6
7
8
+++
tags = [ "a", "b", "c" ]
tags_weight = 22
categories = ["d"]
title = "foo"
categories_weight = 44
+++
Front Matter with weighted tags and categories
说明:上面的例子,获取所有分类时 tags
的分类会在 categories
之前
分类渲染
显示 tags
标签的所有分类,使用 .GetTerms
:
1
2
3
4
5
< ul >
{{ range (. GetTerms "tags" ) }}
< li >< a href = "{{ .Permalink }}" >{{ . LinkTitle }}</ a ></ li >
{{ end }}
</ ul >
或者,使用 Site.Taxonomies.tags
:
1
2
3
4
5
< ul >
{{ range . Site . Taxonomies . tags }}
< li >< a href = "{{ .Page.Permalink }}" >{{ . Page . Title }}</ a > {{ . Count }}</ li >
{{ end }}
</ ul >
或者,使用 .GetPage
:
1
2
3
4
5
6
7
8
{{ $taxo := "tags" }}
< ul class = "{{ $taxo }}" >
{{ with ( $ . Site . GetPage ( printf "/%s" $taxo )) }}
{{ range . Pages }}
< li >< a href = "{{ .Permalink }}" >{{ . Title }}</ a ></ li >
{{ end }}
{{ end }}
</ ul >
series
显示 series=golang
分类的所有页面:
1
2
3
4
5
< ul >
{{ range . Site . Taxonomies . series . golang }}
< li >< a href = "{{ .Page.RelPermalink }}" >{{ . Page . Title }}</ a ></ li >
{{ end }}
</ ul >
featured
显示所有分类方式,并且显示每个分类方式下的所有分类:
1
2
3
4
5
6
7
8
9
10
11
12
< section id = "menu" >
< ul >
{{ range $key , $taxonomy := . Site . Taxonomies . featured }}
< li >{{ $key }}</ li >
< ul >
{{ range $taxonomy . Pages }}
< li hugo - nav = "{{ .RelPermalink}}" >< a href = "{{ .Permalink}}" >{{ . LinkTitle }}</ a ></ li >
{{ end }}
</ ul >
{{ end }}
</ ul >
</ section >
单页模板
单页模板应用于具体的单独页面,一般是最终展示的具有详细内容的页面,例如文章
模板应用可以参阅,
单页模板应用规则
,例如 layouts/posts/single.html
应用在 content/posts/
下的所有单页:
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
{{ define "main" }}
< section id = "main" >
< h1 id = "title" >{{ . Title }}</ h1 >
< div >
< article id = "content" >
{{ . Content }}
</ article >
</ div >
</ section >
< aside id = "meta" >
< div >
< section >
< h4 id = "date" > {{ . Date . Format "Mon Jan 2, 2006" }} </ h4 >
< h5 id = "wordcount" > {{ . WordCount }} Words </ h5 >
</ section >
{{ with . GetTerms "topics" }}
< ul id = "topics" >
{{ range . }}
< li >< a href = "{{ .RelPermalink }}" >{{ . LinkTitle }}</ a ></ li >
{{ end }}
</ ul >
{{ end }}
{{ with . GetTerms "tags" }}
< ul id = "tags" >
{{ range . }}
< li >< a href = "{{ .RelPermalink }}" >{{ . LinkTitle }}</ a ></ li >
{{ end }}
</ ul >
{{ end }}
</ div >
< div >
{{ with . PrevInSection }}
< a class = "previous" href = "{{.Permalink}}" > {{. Title }}</ a >
{{ end }}
{{ with . NextInSection }}
< a class = "next" href = "{{.Permalink}}" > {{. Title }}</ a >
{{ end }}
</ div >
</ aside >
{{ end }}
视图模板
Hugo可以呈现内容的其他视图,这在列表和摘要视图中特别有用
以下是视图模板的常见用例:
希望在主页上显示每种类型的内容,但只有有限的摘要视图
在分类法列表页面上显示页面列表
创建视图内容
下面的目录表示,为 content/posts/
和 content/project/
目录下的单页,创建两个视图模板
li.html
summary.html
1
2
3
4
5
6
7
8
9
▾ layouts/
▾ posts/
li.html
single.html
summary.html
▾ project/
li.html
single.html
summary.html
同样也可以为所有单页定义为默认:
1
2
3
4
5
▾ layouts/
▾ _default/
li.html
single.html
summary.html
所有视图模板按照下面的优先级:
/layouts/<TYPE>/<VIEW>.html
/layouts/_default/<VIEW>.html
/themes/<THEME>/layouts/<TYPE>/<VIEW>.html
/themes/<THEME>/layouts/_default/<VIEW>.html
示例:列表视图
列表模板 layouts/_default/list.html
渲染了 summary.html
视图模板
1
2
3
4
5
6
7
8
< main id = "main" >
< div >
< h1 id = "title" >{{ . Title }}</ h1 >
{{ range . Pages }}
{{ . Render "summary" }}
{{ end }}
</ div >
</ main >
layouts/_default/summary.html
1
2
3
4
5
6
7
8
9
10
< article class = "post" >
< header >
< h2 >< a href = '{{ .Permalink }}' > {{ . Title }}</ a > </ h2 >
< div class = "post-meta" >{{ . Date . Format "Mon, Jan 2, 2006" }} - {{ . FuzzyWordCount }} Words </ div >
</ header >
{{ . Summary }}
< footer >
< a href = '{{ .Permalink }}' >< nobr > Read more → </ nobr ></ a >
</ footer >
</ article >
数据模板
Hugo 支持 YAML
XML
JSON
和 TOML
格式的数据加载,数据模板约定在 data
目录下
查看官方视频(英文):
VIDEO
格式约定
数据文件夹应该存储额外的数据,以供 Hugo 在生成站点时使用。
数据文件不是用来生成独立页面的,它们应该作为补充内容文件:
头部注释的字段太多时,继承在数据文件中
显示大数据集的模板,如图表
所有文件后缀必须是下列之一:yml
yaml
json
xml
toml
访问数据文件通过 .Site.Data.<filename>
或者 .Site.Data <filename>
,所有文件名必须是 字母
或者 _
开头,例如:
数据文件
模板代码
x123.json
{{ index .Site.Data "x123" }}
_123.json
{{ index .Site.Data "_123" }}
x-123.json
{{ index .Site.Data "x-123" }}
示例
访问整个数据文件
存在这样一个数据文件 data/jazz/bass/jacopastorius.yaml
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
discography :
- 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)
可以这样访问到:
1
2
3
{{ range $ . Site . Data . jazz . bass }}
{{ partial "artist.html" . }}
{{ end }}
然后在模板 lauouts/partials/artist.html
中:
1
2
3
4
5
< ul >
{{ range . discography }}
< li >{{ . }}</ li >
{{ end }}
</ ul >
访问某个字段
data/user.yaml
1
2
3
4
5
6
Achievements :
- Can create a Key, Value list from Data File
- Learns Hugo
- Reads documentation
Name : User0123
Short Description : He is a **jolly good** fellow.
模板文件中:
1
2
3
< div > Short Description of {{. Site . Data . user . Name }} :
< p >{{ index . Site . Data . user "Short Description" | markdownify }}</ p >
</ div >
获取远程数据
通过 getJSON
和 getCSV
内置函数获取远程数据
基本使用
1
2
{{ $dataJ := getJSON "url" }}
{{ $dataC := getCSV "separator" "url" }}
前缀和传参
1
2
{{ $dataJ := getJSON "url prefix" "arg1" "arg2" "arg n" }}
{{ $dataC := getCSV "separator" "url prefix" "arg1" "arg2" "arg n" }}
例如:
1
2
3
4
5
{{ $urlPre := "https://api.github.com" }}
{{ $gistJ := getJSON $urlPre "/users/GITHUB_USERNAME/gists" }}
{{ /* 最终结果为 */ }}
{{ $gistJ := getJSON "https://api.github.com/users/GITHUB_USERNAME/gists" }}
添加请求头
在末尾添加一个 map 类型的参数
1
{{ $data := getJSON "https://example.org/api" ( dict "X-List" ( slice "a" "b" "c" )) }}
参考:
dict 函数
和
slice 函数
示例:CSV 文件
layouts/partials/get-csv.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
< table >
< thead >
< tr >
< th > Name </ th >
< th > Position </ th >
< th > Salary </ th >
</ tr >
</ thead >
< tbody >
{{ $url := "https://example.com/finance/employee-salaries.csv" }}
{{ $sep := "," }}
{{ range $i , $r := getCSV $sep $url }}
< tr >
< td >{{ index $r 0 }}</ td >
< td >{{ index $r 1 }}</ td >
< td >{{ index $r 2 }}</ td >
</ tr >
{{ end }}
</ tbody >
</ table >
可选模板
可选模板一般为列表和页面模板中较小的上下文感知组件
查看官方视频(英文):
VIDEO
可选模板按照下列顺序应用:
layouts/partials/*<PARTIALNAME>.html
themes/<THEME>/layouts/partials/*<PARTIALNAME>.html
使用
假设目录关系为:
1
2
3
4
5
6
7
8
9
10
11
12
13
layouts/
└── partials/
├── footer/
│ ├── scripts.html
│ └── site-footer.html
├── head/
│ ├── favicons.html
│ ├── metadata.html
│ ├── prerender.html
│ └── twitter.html
└── header/
├── site-header.html
└── site-nav.html
那么使用模板的代码为:
1
2
3
{{ partial "header/site-header.html" . }}
{{ partial "footer/scripts.html" . }}
......
说明:结尾的 .
表示将当前上下文传入可选模板
返回值
1
2
{{ /* layouts/partials/GetFeatured.html */ }}
{{ return first . ( where site . RegularPages "Params.featured" true ) }}
1
2
3
4
{{ /* layouts/index.html */ }}
{{ range partial "GetFeatured.html" 5 }}
[...]
{{ end }}
说明:上面的例子返回前 5 个头部注释 featured=true
的页面
内联
1
2
3
4
5
6
Value : {{ partial "my-inline-partial.html" . }}
{{ define "partials/my-inline-partial.html" }}
{{ $value := 32 }}
{{ return $value }}
{{ end }}
缓存
1
{{ partialCached "footer.html" . . Params . country . Params . province }}
说明:上面的例子缓存 layouts/partials/footer.html
模板,并保存了当前上下文
、Params.country
、Params.province
变量
短代码模板
Hugo 提供短代码模板,在 .md
文件中,使用这些短代码,将会渲染短代码模板在内容中
为保持本地 Markdown 文件的可读性,本文站点不使用短代码,有需求可参考官方文档:
短代码模板
或者查看官方视频(英文):
VIDEO
本地文件模板
有时候,你可能并不想按照 Hugo 的约定定义和使用模板,这时你可以:
使用
readDir 函数
加载一个目录下的所有模板
使用
readFile 函数
加载某个模板
readDir 函数
返回一个 os.FileInfo
数组,它将文件的路径作为单个字符串参数
readFile 函数
从磁盘读取文件,并将其转换为字符串,以供其他 Hugo 函数操作或按原样添加
readFile 接受 文件/文件路径 作为传递给函数的参数。
在模板中使用 readFile 函数,请确保路径相对于Hugo项目的根目录
在短代码中使用 readFile 函数,请确保路径为绝对路径
404 模板
示例 layouts/404.html
:
1
2
3
4
5
6
7
{{ define "main" }}
< main id = "main" >
< div >
< h1 id = "title" >< a href = "{{ " " | relURL }} " > Go Home </ a ></ h1 >
</ div >
</ main >
{{ end }}
菜单模板
定义了
站点菜单
之后,使用
菜单变量和方法
渲染菜单:
菜单可以是自动生成、头部注释定义、或者站点配置
菜单可以扁平化或者层级化
使用国际化
示例
定义一个菜单模板 layouts/partials/menu.html
:
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
{{ - $page := . page }}
{{ - $menuID := . menuID }}
{{ - with index site . Menus $menuID }}
< nav >
< ul >
{{ - partial "inline/menu/walk.html" ( dict "page" $page "menuEntries" .) }}
</ ul >
</ nav >
{{ - end }}
{{ - define "partials/inline/menu/walk.html" }}
{{ - $page := . page }}
{{ - range . menuEntries }}
{{ - $attrs := dict "href" . URL }}
{{ - if $page . IsMenuCurrent . Menu . }}
{{ - $attrs = merge $attrs ( dict "class" "active" "aria-current" "page" ) }}
{{ - else if $page . HasMenuCurrent . Menu .}}
{{ - $attrs = merge $attrs ( dict "class" "ancestor" "aria-current" "true" ) }}
{{ - end }}
< li >
< a
{{ - range $ k , $ v := $ attrs }}
{{ - with $ v }}
{{ - printf " % s = % q " $ k $ v | safeHTMLAttr }}
{{ - end }}
{{ - end - }}
>{{ or ( T . Identifier ) . Name | safeHTML }}</ a >
{{ - with . Children }}
< ul >
{{ - partial "inline/menu/walk.html" ( dict "page" $page "menuEntries" .) }}
</ ul >
{{ - end }}
</ li >
{{ - end }}
{{ - end }}
使用菜单模板 layouts/_default/single.html
:
1
2
{{ partial "menu.html" ( dict "menuID" "main" "page" .) }}
{{ partial "menu.html" ( dict "menuID" "footer" "page" .) }}
菜单参数
layouts/partials/menu.html
:
1
2
3
4
5
{{ - range site . Menus . main }}
< a {{ with .Params.class - }} class = "{{ . }}" {{ end - }} href = "{{ .URL }}" >
{{ . Name }}
</ a >
{{ - end }}
分页模板
分页模板使用两个站点配置:
paginate
:分页数量,默认 10
pagiantePath
:分页的 HTML 生成路径,默认 path
分页渲染
分页模板的渲染使用 .Paginator
上下文变量,仅支持非单页页面
有两种方式渲染:
.Paginator.Pages
:
{{ range (.Paginator 5).Pages }}
.Paginate.Pages
:
{{ range (.Paginate ( first 50 .Pages.ByTitle )).Pages }}
{{ range (.Paginate .RegularPagesRecursive).Pages }}
如果是分组页面:
1
{{ range (. Paginate (. Pages . GroupByDate "2006" )). PageGroups }}
分页生成
.Paginator
包含足够的,分页逻辑处理需要的,分页信息
简单使用
1
2
3
4
{{ template "_internal/pagination.html" . }}
{{ range . Paginator . Pages }}
{{ . Title }}
{{ end }}
分页过滤
1
2
3
4
5
{{ $paginator := . Paginate ( where . Pages "Type" "posts" ) }}
{{ template "_internal/pagination.html" . }}
{{ range $paginator . Pages }}
{{ . Title }}
{{ end }}
说明:.Paginate
的返回值是一个 .Paginator
对象
.Paginator 对象属性
属性名
描述
PageNumber
当前页数量
URL
当前页相对 URL
Pages
当前页所有页面
NumberOfElements
当前页所有元素数量
HasPrev
是否有上一页
HasNext
是否有下一页
Next
指向下一页的 Paginator
First
第一页的 Paginator
Last
最后一页的 Paginator
Pagers
所有页面的 Paginator
PageSize
每页大小
TotalPages
页面总数
TotalNumberOfElements
所有页面元素总数
Hugo提供了RSS 2.0模板,几乎不需要配置,也可以创建自己的RSS模板,
RSS模板应用规则
默认情况下,Hugo 将生成无限数量的 RSS 文件
1
2
3
4
5
author :
name : My Name Here
copyright : This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
International License.
languageCode : en-us
在 header.html
模板中,可以在 <head>
标签中指定 RSS 种子:
1
2
3
{{ range . AlternativeOutputFormats - }}
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` . Rel . MediaType . Type . Permalink $ . Site . Title | safeHTML }}
{{ end - }}
说明:RSS 种子有多种格式
如果只需要 RSS 的链接:
1
2
3
{{ with . OutputFormats . Get "rss" - }}
{{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` . Rel . MediaType . Type . Permalink $ . Site . Title | safeHTML }}
{{ end - }}
生成结果:
1
< link rel = "alternate" type = "application/rss+xml" href = "https://example.com/index.xml" title = "Site Title" >
Sitemap 模板
Hugo 的内置 Sitemap 模板符合
Sitemaps Protocol v0.9
,使用内置的 sitemap.xml
模板生成文件
Sitemap 配置
在站点配置 config.yaml
中的配置及默认值:
1
2
3
4
sitemap :
changefreq : "" # 更新频率,支持的值:always, hourly, daily, weekly, monthly, yearly, never
filename : sitemap.xml # 生成的文件名,默认值:sitemap.xml
priority : -1 # 一个页面到另一个页面的优先级,取值范围:0-1,默认值:-1(忽略)
在页面头部注释中重写:
1
2
3
4
5
6
---
sitemap :
changefreq : weekly
priority : 0.8
title : News
---
重写内置模板
通过在下面两个位置定义模板重写内置的 sitemap.xml
:
layouts/sitemap.xml
layouts/_default/sitemap.xml
通过在下面两个位置定义模板重写内置的 sitemapindex.xml
:
layouts/sitemapindex.xml
layouts/_default/sitemapindex.xml
当对非单页页面进行排序时,使用 .Sitemap.ChangeFreq
和 .Sitemap.Priority
分别访问页面的更新频率和优先级
禁用 Sitemap 生成
1
2
3
# config.yaml
disableKinds : # 支持的值:"page", "home", "section", "taxonomy", "term", "RSS", "sitemap", "robotsTXT", "404"
- sitemap
Robots.txt
搜索引擎通过该文件,可允许进行站点所有内容爬取,进行搜索引擎优化
生成 robots.txt
文件与其他模板的生成一样,通过站点配置 enableRobotsTXT=true
开启
内置模板
Hugo 内置有 robots.txt
,只有以下内容
模板应用规则
/layouts/robots.txt
/themes/<THEME>/layouts/robots.txt
示例:不爬取链接
1
2
3
4
User-agent: *
{{ range .Pages }}
Disallow: {{ .RelPermalink }}
{{ end }}
内置模板
Hugo 附带了一组样板模板,涵盖了静态网站最常见的用例
虽然下面的内部模板的调用类似于
可选模板
,但它们不遵循可选模板的应用规则
Google Analytics
这部分内容用于 Google 对站点进行内容分析,利于 Google 搜索优化
支持谷歌 Analytics,支持
Google Analytics 4
和谷歌通用分析,下面是配置开启和模板使用:
Google Analytics 4 (gtag.js)
1
googleAnalytics : G-MEASUREMENT_ID
谷歌通用分析(analytics.js)
1
googleAnalytics : UA-PROPERTY_ID
使用模板
1
2
3
4
{{ /* 谷歌通用分析 */ }}
{{ template "_internal/google_analytics_async.html" . }}
{{ /* Google Analytics 4 */ }}
{{ template "_internal/google_analytics.html" . }}
注:如果要重写内置模板,使用 .Site.Config.Services.GoogleAnalytics.ID
访问配置的 ID
这个内置模板用于站点评论,disqusShortname
的值来自
Disqus 官网注册的免费服务
站点配置中配置开启
1
disqusShortname : your-disqus-shortname # .Site.DisqusShortname 变量可以访问到这个配置
对大多数站点来说,上面的配置已经足够,但是仍然可以在页面头部注释设置以下内容:
disqus_identifier
disqus_title
disqus_url
使用内置 Disqus 模板
在需要出现评论的地方,在模板文件中添加下面的代码
1
{{ template "_internal/disqus.html" . }}
区分开发与生产环境
可以注意到,当在本地主机上运行Hugo web服务器(即通过Hugo server)时
启用 Disqus 会导致在相关的Disqus帐户上创建不必要的评论内容
可以通过创建 layouts/partials/disqus.html
扩展评论功能的加载:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
< div id = "disqus_thread" ></ div >
< script type = "text/javascript" >
( function () {
// Don't ever inject Disqus on localhost--it creates unwanted
// discussions from 'localhost:1313' on your Disqus account...
if ( window . location . hostname == "localhost" )
return ;
var dsq = document . createElement ( 'script' ); dsq . type = 'text/javascript' ; dsq . async = true ;
var disqus_shortname = '{{ .Site.DisqusShortname }}' ;
dsq . src = '//' + disqus_shortname + '.disqus.com/embed.js' ;
( document . getElementsByTagName ( 'head' )[ 0 ] || document . getElementsByTagName ( 'body' )[ 0 ]). appendChild ( dsq );
})();
</ script >
< noscript > Please enable JavaScript to view the < a href = "https://disqus.com/?ref_noscript" > comments powered by Disqus .</ a ></ noscript >
< a href = "https://disqus.com/" class = "dsq-brlink" > comments powered by < span class = "logo-disqus" > Disqus </ span ></ a >
然后加载评论:
1
{{ partial "disqus.html" . }}
Open Graph
这个内置模板遵从
Open Graph协议
作用是使页面成为社交图中的丰富对象的元数据,这种格式被用于 Facebook 和其他一些网站
内置 Open Graph 模板的页面元数据生成规则
title
和 description
作为标题和描述的元数据
images
的前6个图像作为图像元数据,如果未定义,在
页面绑定资源
中匹配含 feature
cover
thumbnail
名称的图片
例如,有下面的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# config.yaml
params :
description : Text about my cool site
images :
- site-feature-image.jpg
title : My cool site
taxonomies :
series : series
# content/blog/my-post.md 的头部注释
audio : []
date : "2006-01-02"
description : Text about this post
images :
- post-cover.png
series : []
tags : []
title : Post title
videos : []
其他可选的元数据设置:
date
publishDate
lastmod
作为日期元数据
audio
videos
作为音视频元数据,同 images
获取前6个
tags
作为标签元数据,同 images
获取前6个
series
指定系列元数据
使用 Open Graph 内置模板
1
{{ template "_internal/opengraph.html" . }}
用于在页面中生成富媒体内容,当 Twitter 引用站点的页面时,将会显示媒体丰富的链接卡片
例如有下面的配置:
1
2
3
4
5
6
7
8
9
10
11
# config.yaml
params :
description : Text about my cool site
images :
- site-feature-image.jpg
# content/blog/my-post.md 的头部注释
description : Text about this post
images :
- post-cover.png
title : Post title
titile
生成链接标题
description
生成描述,如果未指定使用 summary
images
生成封面图片:
如果页面头部注释未指定,首先在
页面绑定资源
中匹配含 feature
cover
thumbnail
名称的图片
如果未匹配到,匹配 config.yaml
中配置的图片
如果都没找到,生成无图片的链接卡片,图片内容 summary_large_image
替代
站点配置:
1
2
social :
twitter : GoHugoIO # .Site.Social.twitter 可访问这个配置
生成页面将会添加 meta
:
1
< meta name = "twitter:site" content = "@GoHugoIO" />
使用内置模板:
1
{{ template "_internal/twitter_cards.html" . }}
模板 Debugging
使用下面的内置函数输出变量的值:
1
2
3
4
5
6
7
8
9
{{ printf "%#v" $ . Site }}
{{ printf "%#v" . Permalink }}
{{ printf "%#v" . }}
{{ range . Pages }}
{{ printf "%#v" . }}
{{ end }}
注:也可以使用 warnf
替代 printf
内置函数
.GetPage
.AddDate YEARS MONTHS DAYS
:增加时间
1
2
3
4
5
6
7
8
9
{{ $d := "2023-01-31" | time . AsTime }}
{{ $d . AddDate 0 1 0 | time . Format "2006-01-02" }} --> 2023 - 03 - 03
{{ $d := "2024-01-31" | time . AsTime }}
{{ $d . AddDate 0 1 0 | time . Format "2006-01-02" }} --> 2024 - 03 - 02
{{ $d := "2024-02-29" | time . AsTime }}
{{ $d . AddDate 1 0 0 | time . Format "2006-01-02" }} --> 2025 - 03 - 01
See Also
说明:接受三个参数,分别为 年
月
日
.Format FORMAT
:格式化时间
支持头部注释的这三个字段:date
publishDate
lastmod
1
2
{{ /* 假设 PublishDate 为 2017年3月2日 */ }}
{{ . PublishDate . Format "2006-01-02" }} => 2017 - 03 - 02
注:
更多内容参考官方文档
.Get
.Get INDEX
或 .Get KEY
:获取短代码入参
1
{{ $quality := default "100" (. Get 0 ) }}
说明:上面的代码获取短代码第一个参数
.GetPage
.GetPage PATH
:获取指定目录下的页面
1
2
3
4
5
6
7
{{ with . Site . GetPage "/blog" }}{{ . Title }}{{ end }}
{{ with . Site . GetPage "/blog/my-post.md" }}{{ . Title }}{{ end }}
{{ with . Site . GetPage "/blog" }}
{{ with . GetPage "my-post.md" }}{{ . Title }}{{ end }}
{{ end }}
注:在 Hugo v0.45
之前,需要指定页面 Kind
例如:{{ .Site.GetPage "section" "blog" }}
示例:标签链接
1
2
3
4
5
6
< ul class = "most-popular-tags" >
{{ $t := . Site . GetPage "/tags" }}
{{ range first 2 $t . Data . Terms . ByCount }}
< li >{{ . }}</ li >
{{ end }}
</ ul >
PAGE.HasMenuCurrent MENU MENUENTRY
:返回布尔值,在
菜单模板
中查看示例
PAGE.IsMenuCurrent MENU MENUENTRY
:返回布尔值,在
菜单模板
中查看示例
.Param
.Param KEY
:返回站点配置 params
或页面头部注释的某个字段对应的值
1
2
3
4
5
6
7
8
9
10
11
# config.yaml
params :
foo : true
# content/about.md
---
date : "2023-01-01"
foo : false
draft : false
title : About
---
1
2
{{ /* layouts/_default/single.html */ }}
{{ or . Params . foo $ . Site . Params . foo }}
.Render
.Render LAYOUT
:渲染一个视图模板,在
视图模板
查看示例
.RenderString
.RenderString MARKUP
:将 Markdown 标记语言转化为 HTML 字符串
1
2
3
4
5
{{ $optBlock := dict "display" "block" }}
{{ $optOrg := dict "markup" "org" }}
{{ "**Bold Markdown**" | $p . RenderString }}
{{ "**Bold Block Markdown**" | $p . RenderString $optBlock }}
{{ "/italic org mode/" | $p . RenderString $optOrg }}
.Scratch
Scratch是Hugo的一个特性,旨在方便地在Go模板世界中操作数据
它是一个 Page
或 Shortcode
方法,结果数据将被附加到给定的上下文
或者它可以作为存储在变量中的唯一实例存在
使用方式
Hugo v0.43+
有两种使用方式
1. Page/Shortcode 中使用
1
2
3
4
{{ . Scratch . Set "greeting" "bonjour" }}
{{ range . Pages }}
{{ . Scratch . Set "greeting" ( print "bonjour" . Title ) }}
{{ end }}
2. newScratch
1
2
{{ $data := newScratch }}
{{ $data . Set "greeting" "hola" }}
实例方法
.Set
1
{{ $scratch . Set "greeting" "Hello" }}
.Get
1
2
3
{{ $scratch . Set "greeting" "Hello" }}
----
{{ $scratch . Get "greeting" }} > Hello
.Add
字符串:
1
2
3
4
{{ $scratch . Add "greetings" "Hello" }}
{{ $scratch . Add "greetings" "Welcome" }}
----
{{ $scratch . Get "greetings" }} > HelloWelcome
数值:
1
2
3
4
{{ $scratch . Add "total" 3 }}
{{ $scratch . Add "total" 7 }}
----
{{ $scratch . Get "total" }} > 10
切片:
1
2
3
4
{{ $scratch . Add "greetings" ( slice "Hello" ) }}
{{ $scratch . Add "greetings" ( slice "Welcome" "Cheers" ) }}
----
{{ $scratch . Get "greetings" }} > [] interface {}{ "Hello" , "Welcome" , "Cheers" }
.SetInMap
1
2
3
4
{{ $scratch . SetInMap "greetings" "english" "Hello" }}
{{ $scratch . SetInMap "greetings" "french" "Bonjour" }}
----
{{ $scratch . Get "greetings" }} > map [ french : Bonjour english : Hello ]
.DeleteInMap
1
2
3
4
5
6
{{ . Scratch . SetInMap "greetings" "english" "Hello" }}
{{ . Scratch . SetInMap "greetings" "french" "Bonjour" }}
----
{{ . Scratch . DeleteInMap "greetings" "english" }}
----
{{ . Scratch . Get "greetings" }} > map [ french : Bonjour ]
.GetSortedMapValues
1
2
3
4
{{ $scratch . SetInMap "greetings" "english" "Hello" }}
{{ $scratch . SetInMap "greetings" "french" "Bonjour" }}
----
{{ $scratch . GetSortedMapValues "greetings" }} > [ Hello Bonjour ]
.Delete
1
2
3
{{ $scratch . Set "greeting" "Hello" }}
----
{{ $scratch . Delete "greeting" }}
.Values
返回原始备份的 map,只能在 newScratch
创建的实例中使用
.Store
Page.Store
方法返回一个 Scratch 来存储和操作数据
但是与 .Scratch
方法相反,这个 Scratch 在服务器重建时不会重置
返回的实例有如下方法:
.Set
1
{{ . Store . Set "greeting" "Hello" }}
.Get
1
2
3
{{ . Store . Set "greeting" "Hello" }}
----
{{ . Store . Get "greeting" }} → Hello
.Add
字符串:
1
2
3
4
{{ . Store . Add "greetings" "Hello" }}
{{ . Store . Add "greetings" "Welcome" }}
----
{{ . Store . Get "greetings" }} → HelloWelcome
数值:
1
2
3
4
{{ . Store . Add "total" 3 }}
{{ . Store . Add "total" 7 }}
----
{{ . Store . Get "total" }} → 10
切片:
1
2
3
4
{{ . Store . Add "greetings" ( slice "Hello" ) }}
{{ . Store . Add "greetings" ( slice "Welcome" "Cheers" ) }}
----
{{ . Store . Get "greetings" }} → [] interface {}{ "Hello" , "Welcome" , "Cheers" }
.SetInMap
1
2
3
4
{{ . Store . SetInMap "greetings" "english" "Hello" }}
{{ . Store . SetInMap "greetings" "french" "Bonjour" }}
----
{{ . Store . Get "greetings" }} → map [ french : Bonjour english : Hello ]
.DeleteInMap
1
2
3
4
5
{{ . Store . SetInMap "greetings" "english" "Hello" }}
{{ . Store . SetInMap "greetings" "french" "Bonjour" }}
{{ . Store . DeleteInMap "greetings" "english" }}
----
{{ . Store . Get "greetings" }} → map [ french : Bonjour ]
.GetSortedMapValues
1
2
3
4
{{ . Store . SetInMap "greetings" "english" "Hello" }}
{{ . Store . SetInMap "greetings" "french" "Bonjour" }}
----
{{ . Store . GetSortedMapValues "greetings" }} → [ Hello Bonjour ]
.Delete
1
2
3
{{ . Store . Set "greeting" "Hello" }}
----
{{ . Store . Delete "greeting" }}
.Unix
所有时间类型的实例都支持,返回时间戳,有四种精度:
.Unix
:秒
.UnixMilli
:毫秒
.UnixMicro
:微秒
.UnixNamo
:纳秒
absLangURL
absLangURL INPUT
:根据输入,返回带当前语言的绝对路径
1
2
3
4
5
6
7
8
{{ /* baseURL= https://example.org/ */ }}
{{ absLangURL "" }} → "https://example.org/en/"
{{ absLangURL "articles" }} → "https://example.org/en/articles"
{{ absLangURL "style.css" }} → "https://example.org/en/style.css"
{{ /* baseURL= https://example.org/docs/ */ }}
{{ absLangURL "" }} → "https://example.org/docs/en/"
{{ absLangURL "articles" }} → "https://example.org/docs/en/articles"
{{ absLangURL "style.css" }} → "https://example.org/docs/en/style.css"
如果 input 以 /
开头:
1
2
3
4
5
6
7
8
{{ /* baseURL= https://example.org/ */ }}
{{ absLangURL "/" }} → "https://example.org/en/"
{{ absLangURL "/articles" }} → "https://example.org/en/articles"
{{ absLangURL "/style.css" }} → "https://example.org/en/style.css"
{{ /* baseURL= https://example.org/docs/ */ }}
{{ absLangURL "/" }} → "https://example.org/en/"
{{ absLangURL "/articles" }} → "https://example.org/en/articles"
{{ absLangURL "/style.css" }} → "https://example.org/en/style.css"
absURL
同
absLangURL
,区别在于没有 /en
after
after INDEX COLLECTION
:用于切片,忽略前几个元素
1
2
3
4
5
{{ $data := slice "one" "two" "three" "four" }}
{{ range after 2 $data }}
{{ . }}
{{ end }}
→ [ "three" , "four" ]
anchorize
anchorize INPUT
:处理 Markdown 标题锚点,去除特殊字符,空格转为 -
1
2
3
4
5
6
{{ anchorize "This is a header" }} --> "this-is-a-header"
{{ anchorize "This is also a header" }} --> "this-is-also----a-header"
{{ anchorize "main.go" }} --> "maingo"
{{ anchorize "Article 123" }} --> "article-123"
{{ anchorize "<- Let's try this, shall we?" }} --> "--lets-try-this-shall-we"
{{ anchorize "Hello, 世界" }} --> "hello-世界"
append
COLLECTION | append VALUE [VALUE]...
或 COLLECTION | append COLLECTION
:合并切片
1
2
3
{{ $s := slice "a" "b" "c" }}
{{ $s = $s | append "d" "e" }}
→ [ "a" "b" "c" "d" "e" ]
1
2
3
{{ $s := slice "a" "b" "c" }}
{{ $s = $s | append ( slice "d" "e" ) }}
→ [ "a" "b" "c" "d" "e" ]
apply
apply COLLECTION FUNCTION [PARAM...]
:给切片/映射添加一个给定的函数
页面头部注释:
1
2
3
---
names : [ "Derek Perkins" , "Joe Bergevin" , "Tanner Linsley" ]
---
添加 urlize
方法:
1
{{ apply . Params . names "urlize" "." }}
遍历时使用:
1
2
3
{{ range . Params . names }}
{{ . | urlize }}
{{ end }}
base64
base64Decode INPUT
或 base64Encode INPUT
1
2
< p > Hello world = {{ "Hello world" | base64Encode }}</ p >
< p > SGVsbG8gd29ybGQ = {{ "SGVsbG8gd29ybGQ=" | base64Decode }}</ p >
chomp
chomp INPUT
或 strings.Chomp INPUT
:删除所有尾随换行符
1
{{ chomp "<p>Blockhead</p>\n" }} → "<p>Blockhead</p>"
complement
complement COLLECTION [COLLECTION]...
或 collections.Complement COLLECTION [COLLECTION]...
:返回不属于前面集合的元素
1
2
3
4
5
{{ $c1 := slice 3 }}
{{ $c2 := slice 4 5 }}
{{ $c3 := slice 1 2 3 4 5 }}
{{ complement $c1 $c2 $c3 }} → [ 1 2 ]
cond
cond CONTROL VAR1 VAR2
:三元运算符
1
{{ cond ( eq ( len $geese ) 1 ) "goose" "geese" }}
countrunes
countrunes INPUT
或 strings.CountRunes INPUT
:返回字符串 rune
类型切片
1
2
{{ "Hello, 世界" | countrunes }}
{{ /* 输出 8 长度的 rune 切片 */ }}
countwords
countwords INPUT
:返回字符串单词长度
1
{{ "Hugo is a static site generator." | countwords }} → 6
crypto.FNV32a
crypto.FNV32a STRING
:返回给定字符串的 32 位 FNV 值
1
{{ crypto . FNV32a "Hello world" }} → 1498229191
default
default DEFAULT INPUT
:如果第二个值为 nil
,返回第一个值
1
2
{{ index . Params "font" | default "Roboto" }}
{{ default "Roboto" ( index . Params "font" ) }}
delimit
delimit COLLECTION DELIMIT LAST
:循环遍历任何数组、切片或映射,并返回由分隔符分隔的所有值组成的字符串
某个页面头部注释为:
1
2
3
4
---
title : I love Delimit
tags : [ "tag1" , "tag2" , "tag3" ]
---
那么:
1
2
Tags : {{ delimit . Params . tags ", " }} → Tags : tag1 , tag2 , tag3
Tags : {{ delimit . Params . tags ", " ", and " }} → Tags : tag1 , tag2 , and tag3
dict
dict KEY VALUE [KEY VALUE]...
:定义一个 map
duration
duration TIME_UNIT DURATION_NUMBER
:返回 time.Duration
结构体
1
2
{{ printf "There are %.0f seconds in one day." ( duration "hour" 24 ). Seconds }}
<!-- Output : There are 86400 seconds in one day . -->
支持的单位:
时间周期
支持单位
hours
hour
h
minutes
minute
m
seconds
second
s
milliseconds
millisecond
ms
microseconds
microsecond
us
µs
nanoseconds
nanosecond
ns
echoParam
echoParam DICTIONARY KEY
:输出映射的某个字段值
emojify
emojify INPUT
:通过 Emoji 表情处理器运行一个字符串
例如: I :heart: Hugo!
=> I ❤️ Hugo!
eq
eq ARG1 ARG2
:判断两个值是否相等
errorf & warnf
errorf FORMAT INPUT
或 warnf FORMAT INPUT
:输出错误/警告日志
1
2
{{ errorf "Failed to handle page %q" . Path }}
{{ warnf "You should update the shortcodes in %q" . Path }}
fileExists
os.FileExists PATH
或 fileExists PATH
:判断目录/文件是否存在
假设目录关系为:
1
2
3
4
5
6
content/
├── about.md
├── contact.md
└── news/
├── article-1.md
└── article-2.md
1
2
3
4
5
6
7
{{ os . FileExists "content" }} --> true
{{ os . FileExists "content/news" }} --> true
{{ os . FileExists "content/news/article-1" }} --> false
{{ os . FileExists "content/news/article-1.md" }} --> true
{{ os . FileExists "news" }} --> true
{{ os . FileExists "news/article-1" }} --> false
{{ os . FileExists "news/article-1.md" }} --> true
findRE
findRE PATTERN INPUT [LIMIT]
或 strings.FindRE PATTERN INPUT [LIMIT]
:正则匹配字符串,返回匹配的字符串切片
1
2
{{ findRE `(?s)<h2.*?>.*?</h2>` . Content }}
{{ findRE `(?s)<h2.*?>.*?</h2>` . Content 1 }}
first
first LIMIT COLLECTION
:返回指定数量的数组
基本使用
1
2
3
{{ range first 10 . Pages }}
{{ . Render "summary" }}
{{ end }
结合 where 使用
1
2
3
{{ range first 5 ( where site . RegularPages "Type" "in" site . Params . mainSections ). ByTitle }}
{{ . Content }}
{{ end }}
float
float INPUT
:通过给定的值创建一个浮点数
1
{{ float "1.23" }} → 1.23
ge
ge ARG1 ARG2
:返回 ARG1 >= ARG2 的布尔值
getenv
os.Getenv VARIABLE
或 getenv VARIABLE
:返回环境变量
1
2
{{ os . Getenv "HOME" }} --> /home/victor
{{ os . Getenv "USER" }} --> victor
group
PAGES | group KEY
:页面集合分组
1
2
{{ $new := . Site . RegularPages | first 10 | group "New" }}
{{ $old := . Site . RegularPages | last 10 | group "Old" }}
gt
gt ARG1 ARG2
:返回 ARG1 > ARG2 的布尔值
hasPrefix
hasPrefix STRING PREFIX
:判断字符串前缀
1
{{ hasPrefix "Hugo" "Hu" }} → true
highlight
transform.Highlight INPUT LANG [OPTIONS]
或 highlight INPUT LANG [OPTIONS]
:渲染高亮代码块
参考语法高亮
hmac
crypto.HMAC HASH_TYPE KEY MESSAGE [ENCODING]
或 hmac HASH_TYPE KEY MESSAGE [ENCODING]
:使用密钥对消息进行签名加密
1
2
3
4
5
6
7
8
{{ hmac "sha256" "Secret key" "Secret message" }}
5 cceb491f45f8b154e20f3b0a30ed3a6ff3027d373f85c78ffe8983180b03c84
{{ hmac "sha256" "Secret key" "Secret message" "hex" }}
5 cceb491f45f8b154e20f3b0a30ed3a6ff3027d373f85c78ffe8983180b03c84
{{ hmac "sha256" "Secret key" "Secret message" "binary" | base64Encode }}
XM60kfRfixVOIPOwow7Tpv8wJ9Nz + Fx4 /+ iYMYCwPIQ =
htmlEscape
htmlEscape INPUT
:返回带有转义字符的 HTML 字符串
1
{{ htmlEscape "Hugo & Caddy > WordPress & Apache" }} → "Hugo & Caddy > WordPress & Apache"
htmlUnescape
htmlUnescape INPUT
:与 htmlEscape
相反
1
{{ htmlUnescape "Hugo & Caddy > WordPress & Apache" }} → "Hugo & Caddy > WordPress & Apache"
hugo
访问 Hugo 关系数据,
参考官方文档
humanize
humanize INPUT
:返回人类易读的字符串
1
2
3
4
{{ humanize "my-first-post" }} → "My first post"
{{ humanize "myCamelPost" }} → "My camel post"
{{ humanize "52" }} → "52nd"
{{ humanize 103 }} → "103rd"
i18n
i18n KEY
或 T KEY
或 lang.Translate KEY
:返回国际化配置的值
参考国际化
images
提供图像过滤和一些关系处理函数:
1
2
{{ $logoFilter := ( images . Overlay $logo 50 50 ) }} // 左上角起点选取50x50的图像区域
{{ $img := $img | images . Filter $logoFilter }}
添加文字:
1
2
3
4
5
6
7
8
{{ $img := resources . Get "/images/background.png" }}
{{ $img = $img . Filter ( images . Text "Hugo rocks!" ( dict
"color" "#ffffff"
"size" 60
"linespacing" 2
"x" 10
"y" 20
))}}
更多关系函数参考官方文档
in
in SET ITEM
:判断元素是否在切片中
index
index COLLECTION INDEXES
:取出切片/映射指定的元素/字段值
1
2
3
4
5
6
7
{{ $map := dict "a" 100 "b" 200 "c" ( slice 10 20 30 ) }}
{{ index $map "c" 1 }} => 20
{{ $map := dict "a" 100 "b" 200 "c" ( dict "d" 10 "e" 20 ) }}
{{ index $map "c" "e" }} => 20
// 或者
{{ $slice := slice "c" "e" }}
{{ index $map $slice }} => 20
int
int INPUT
:字符串转整型
intersect
intersect SET1 SET2
:两个切片取交集,返回 SET1 中的元素
isset
isset COLLECTION INDEX
或 isset COLLECTION KEY
:切片/映射是否有指定的元素/字段
jsonify
jsonify INPUT
或 jsonify OPTIONS INPUT
:JSON 序列化
1
2
3
{{ dict "title" . Title "content" . Plain | jsonify }}
{{ dict "title" . Title "content" . Plain | jsonify ( dict "indent" " " ) }}
{{ dict "title" . Title "content" . Plain | jsonify ( dict "prefix" " " "indent" " " ) }}
可选参数,接受一个 map,支持的 Key:
Key
默认值
描述
indent
""
使用缩进
prefix
""
使用前缀
noHTMLEscape
false
禁用字符串转义
lang
提供国际化处理的一些函数
lang.FormatAccounting PRECISION, CURRENCY, NUMBER
:返回给定数字的,指定国家的,指定精度的,货币表示
1
{{ 512.5032 | lang . FormatAccounting 2 "NOK" }} ---> NOK512 .50
lang.FormatCurrency PRECISION, CURRENCY, NUMBER
:同 FormatAccounting
,区别在于以货币符号表示
1
{{ 512.5032 | lang . FormatCurrency 2 "USD" }} ---> $512 .50
lang.FormatNumber PRECISION, NUMBER
:处理数值精度
1
{{ 512.5032 | lang . FormatNumber 2 }} ---> 512.50
lang.FormatNumberCustom PRECISION, NUMBER, OPTIONS
:同 FormatNumber
,提供可选项含更丰富的功能
1
2
3
4
5
{{ lang . FormatNumberCustom 2 12345.6789 }} ---> 12 , 345.68
{{ lang . FormatNumberCustom 2 12345.6789 "- , ." }} ---> 12.345 , 68
{{ lang . FormatNumberCustom 6 - 12345.6789 "- ." }} ---> - 12345.678900
{{ lang . FormatNumberCustom 0 - 12345.6789 "- . ," }} ---> - 12 , 346
{{ - 98765.4321 | lang . FormatNumberCustom 2 }} ---> - 98 , 765.43
lang.FormatPercent PRECISION, NUMBER
:数值加 %
1
{{ 512.5032 | lang . FormatPercent 2 }} ---> 512.50 %
Translate
lang.Translate ID, ARGS
:获取国际化配置,同
i18n 和 T
Merge
lang.Merge FROM TO
:合并来自其他语言的缺失翻译
1
{{ $pages := . Site . RegularPages | lang . Merge $frSite . RegularPages | lang . Merge $enSite . RegularPages }}
last
last INDEX COLLECTION
:返回切片中倒数 INDEX 个元素之后的所有元素在一个数组中
1
2
3
{{ range last 10 . Pages }}
{{ . Render "summary" }}
{{ end }}
le
le ARG1 ARG2
:返回 ARG1 <= ARG2 的布尔值
len
len INPUT
:返回字符串、数组/切片、映射、数组指针、信道长度
lower
len INPUT
或 strings.ToLower INPUT
:字符串转小写
1
2
{{ lower "BatMan" }} → "batman"
{{ "BatMan" | lower }} → "batman"
lt
lt ARG1 ARG2
:返回 ARG1 < ARG2 的布尔值
markdownify
markdownify INPUT
:渲染 Markdown 字符串为 HTML
1
{{ . Title | markdownify }}
Math
用于数值计算的一些函数
函数
描述
示例
add
累加,如果一个为浮点数,结果为浮点数
{{ add 12 3 2 }}
→ 17
sub
累减,如果一个为浮点数,结果为浮点数
{{ sub 12 3 2 }}
→ 7
mul
累乘,如果一个为浮点数,结果为浮点数
{{ mul 12 3 2 }}
→ 72
div
累除,如果一个为浮点数,结果为浮点数
{{ div 12 3 2 }}
→ 2
mod
求余
{{ mod 15 3 }}
→ 0
z
modBool
是否整除
{{ modBool 15 3 }}
→ true
math.Ceil
向上取整
{{ math.Ceil 2.1 }}
→ 3
math.Floor
向下取整
{{ math.Floor 1.9 }}
→ 1
math.Log
求对数
{{ math.Log 42 }}
→ 3.737
math.Max
取最大值
{{ math.Max 12 3 2 }}
→ 12
math.Min
取最小值
{{ math.Min 12 3 2 }}
→ 2
math.Pow
求幂
{{ math.Pow 2 3 }}
→ 8
math.Round
四舍五入取整
{{ math.Round 1.5 }}
→ 2
math.Sqrt
平方根
{{ math.Sqrt 81 }}
→ 9
md5
md5 INPUT
:md5 加密字符串
merge
collections.Merge MAP MAP...
或 merge MAP MAP...
:合并映射,相同字段后面的覆盖前面的
1
2
3
4
5
6
7
8
9
{{ $m1 := dict "x" "foo" }}
{{ $m2 := dict "x" "bar" "y" "wibble" }}
{{ $m3 := dict "x" "baz" "y" "wobble" "z" ( dict "a" "huey" ) }}
// example
{{ $merged := merge $m1 $m2 $m3 }}
{{ $merged . x }} --> baz
{{ $merged . y }} --> wobble
{{ $merged . z . a }} --> huey
ne
ne ARG1 ARG2
:返回 ARG1 != ARG2 的布尔值
now
now
:返回当前时间
os.Stat
os.Stat PATH
:返回文件目录/文件信息
1
2
3
4
5
6
7
8
{{ $f := os . Stat "README.md" }}
{{ $f . IsDir }} --> false ( bool )
{{ $f . ModTime }} --> 2021 - 11 - 25 10 : 06 : 49.315429236 - 0800 PST ( time . Time )
{{ $f . Name }} --> README . md ( string )
{{ $f . Size }} --> 241 ( int64 )
{{ $d := os . Stat "content" }}
{{ $d . IsDir }} --> true ( bool )
partialCached
partialCached LAYOUT INPUT [VARIANT...]
:缓存
可选模板
,避免多个页面重复渲染
path
路由处理的相关函数
Base
path.Base PATH
:返回最后一级路由
1
2
3
4
{{ path . Base "a/news.html" }} → "news.html"
{{ path . Base "news.html" }} → "news.html"
{{ path . Base "a/b/c" }} → "c"
{{ path . Base "/x/y/z/" }} → "z"
Dir
path.Dir PATH
:返回去除最后一级路由的路由
1
2
3
4
{{ path . Dir "a/news.html" }} → "a"
{{ path . Dir "news.html" }} → "."
{{ path . Dir "a/b/c" }} → "a/b"
{{ path . Dir "/x/y/z" }} → "/x/y"
Ext
path.Ext PATH
:返回最后一级路由后缀(如果有)
1
{{ path . Ext "a/b/c/news.html" }} → ".html"
Join
path.Join ELEMENT...
:合并多个路由
1
2
3
{{ path . Join "partial" "news.html" }} → "partial/news.html"
{{ path . Join "partial/" "news.html" }} → "partial/news.html"
{{ path . Join "foo/baz" "bar" }} → "foo/baz/bar"
Split
path.Split PATH
:通过 /
切割
Base
和
Dir
1
2
3
{{ $dirFile := path . Split "a/news.html" }} → $dirFile . Dir → "a/" , $dirFile . File → "news.html"
{{ $dirFile := path . Split "news.html" }} → $dirFile . Dir → "" , $dirFile . File → "news.html"
{{ $dirFile := path . Split "a/b/c" }} → $dirFile . Dir → "a/b/" , $dirFile . File → "c"
reflect.IsMap
reflect.IsMap INPUT
:判断是否是 map
1
2
{{ reflect . IsMap ( dict "key" "value" ) }} → true
{{ reflect . IsMap "yo" }} → false
reflect.IsSlice
reflect.IsMap INPUT
:判断是否是 slice 切片
1
2
{{ reflect . IsSlice ( slice 1 2 3 ) }} → true
{{ reflect . IsSlice "yo" }} → false
relLangURL
relLangURL INPUT
:INPUT 目录/文件,进行当前语言路径处理后,最终的相对地址
1
2
3
4
5
6
7
8
{{ /* baseURL = https://example.org/ */ }}
{{ relLangURL "" }} → / en /
{{ relLangURL "articles" }} → / en / articles
{{ relLangURL "style.css" }} → / en / style . css
{{ /* baseURL = https://example.org/docs/ */ }}
{{ relLangURL "" }} → / docs / en /
{{ relLangURL "articles" }} → / docs / en / articles
{{ relLangURL "style.css" }} → / docs / en / style . css
如果是 /
开头:
1
2
3
4
5
6
7
8
{{ /* baseURL = https://example.org/ */ }}
{{ relLangURL "/" }} → / en /
{{ relLangURL "/articles" }} → / en / articles
{{ relLangURL "/style.css" }} → / en / style . css
{{ /* baseURL = https://example.org/docs/ */ }}
{{ relLangURL "/" }} → / en /
{{ relLangURL "/articles" }} → / en / articles
{{ relLangURL "/style.css" }} → / en / style . css
relref
relref . PAGE
:返回页面的相对永久链接
1
2
3
4
5
6
7
8
9
10
{{ relref . "about" }}
{{ relref . "about#anchor" }}
{{ relref . "about.md" }}
{{ relref . "about.md#anchor" }}
{{ relref . "#anchor" }}
{{ relref . "/blog/my-post" }}
{{ relref . "/blog/my-post.md" }}
{{ relref . ( dict "path" "about.md" "lang" "fr" ) }}
{{ relref . ( dict "path" "about.md" "outputFormat" "rss" ) }}
relURL
relURL INPUT
:同 relLangURL
,但是忽略语言处理
1
2
3
4
5
6
7
8
{{ /* baseURL = https://example.org/ */ }}
{{ relLangURL "" }} → /
{{ relLangURL "articles" }} → / articles
{{ relLangURL "style.css" }} → / style . css
{{ /* baseURL = https://example.org/docs/ */ }}
{{ relLangURL "" }} → / docs /
{{ relLangURL "articles" }} → / docs / articles
{{ relLangURL "style.css" }} → / docs / style . css
如果是 /
开头:
1
2
3
4
5
6
7
8
{{ /* baseURL = https://example.org/ */ }}
{{ relLangURL "/" }} → /
{{ relLangURL "/articles" }} → / articles
{{ relLangURL "/style.css" }} → / style . css
{{ /* baseURL = https://example.org/docs/ */ }}
{{ relLangURL "/" }} → /
{{ relLangURL "/articles" }} → / articles
{{ relLangURL "/style.css" }} → / style . css
replace
replace INPUT OLD NEW [LIMIT]
或 strings.Replace INPUT OLD NEW [LIMIT]
:替换字符串中匹配的指定子串
1
2
3
4
`{{ replace "Batman and Robin" "Robin" "Catwoman" }}`
→ "Batman and Catwoman"
{{ replace "aabbaabb" "a" "z" 2 }} → "zzbbaabb"
replaceRE
replaceRE PATTERN REPLACEMENT INPUT [LIMIT]
或 strings.ReplaceRE PATTERN REPLACEMENT INPUT [LIMIT]
:同 replace
,匹配为正则匹配
1
2
3
4
5
6
7
8
{{ $s := "a-b--c---d" }}
{{ replaceRE `(-{2,})` "-" $s }} → a - b - c - d
{{ $s := "a-b--c---d" }}
{{ replaceRE `(-{2,})` "-" $s 1 }} → a - b - c --- d
{{ $s := "http://gohugo.io/docs" }}
{{ replaceRE "^https?://([^/]+).*" "$1" $s }} → gohugo . io
safeCSS
safeCSS INPUT
:避免 CSS 字符串特殊字符被转义
safeHTML
safeHTML INPUT
:避免 HTML 字符串特殊字符被转义
例如,config.yaml
:
1
2
copyright : © 2015 Jane Doe . < a href = "https://creativecommons.org/licenses/by/4.0/" > Some
rights reserved </ a >.
使用 safeHTML
时:
1
2
{{ . Site . Copyright | safeHTML }}
→ © 2015 Jane Doe . < a href = "https://creativecommons.org/licenses/by/4.0/" > Some rights reserved </ a >.
不使用时:
1
2
{{ . Site . Copyright }}
→ "© 2015 Jane Doe. <a href="https://creativecommons.org/licenses by/4.0/">Some rights reserved</a>."
safeHTMLAttr
safeHTMLAttr INPUT
:同 safeHTML
,区别在于该函数用于 HTML 标签属性
例如,config.yaml
:
1
2
3
4
menu :
main :
- name : IRC
url : "irc://irc.freenode.net/#golang"
如果不使用 safeHTMLAttr
:
1
2
3
{{ range site . Menus . main }}
< a href = "{{ .URL }}" >{{ . Name }}</ a > → < a href = "#ZgotmplZ" > IRC </ a >
{{ end }}
正确使用:
1
2
3
{{ range site . Menus . main }}
< a {{ printf " href = % q " .URL | safeHTMLAttr }}>{{ . Name }}</ a >
{{ end }}
safeJS
safeJS INPUT
:同 safeCSS
和 safeHTML
,区别在于该函数用于 <script>
标签
safeURL
safeURL INPUT
:同 safeHTMLAttr
,区别在于该函数只用于处理 URL
seq
seq LAST
或 seq FIRST LAST
或 seq FIRST INCREMENT LAST
:生成等差数列切片
3 → [1 2 3]
1 2 4 → [1 3]
-3 → [-1 -2 -3]
1 4 → [1 2 3 4]
1 -2 → [1 0 -1 -2]
1
2
{{ range after 1 ( seq 20 )}}
{{ end }}
sha
sha1 INPUT
或 sha256 INPUT
:sha1 或 sha256 加密
shuffle
shuffle COLLECTION
:切片元素顺序随机洗牌
singularize
singularize INPUT
:去除复数形式
1
{{ "cats" | singularize }} → “ cat ”
site
site
或 .Site
:访问站点上下文
slice
slice ITEM...
:生成切片
1
{{ $sliceOfStrings := slice "foo" "bar" "buzz" }}
slicestr
slicestr STRING START [END]
或 strings.SliceString STRING START [END]
:剪切字符串子串
1
2
{{ slicestr "BatMan" 3 }} → “ Man ”
{{ slicestr "BatMan" 0 3 }} → “ Bat ”
sort
sort INPUT
:对映射、数组、切片进行按字母排序,返回一个切片
split
split STRING DELIM
:按指定分隔符,将字符串转为切片
1
2
{{ split "tag1,tag2,tag3" "," }} → [ "tag1" , "tag2" , "tag3" ]
{{ split "abc" "" }} → [ "a" , "b" , "c" ]
string
字符串相关的所有函数
string INPUT
:返回 INPUT 的字符串强制转换
Contains
strings.Contains STRING SUBSTRING
: 字符串是否含指定子串
1
2
{{ strings . Contains "Hugo" "go" }} → true
{{ strings . Contains "Hugo" "Go" }} → false
ContainsAny
strings.ContainsAny STRING CHARACTERS
:字符串是否含指定子串的任意一个字符
1
2
{{ strings . ContainsAny "Hugo" "gm" }} → true
{{ strings . ContainsAny "Hugo" "Gm" }} → false
Count
strings.Count SUBSTR STRING
:字符串指定子串出现的次数
示例
结果
`{{ “aaabaab”
strings.Count “a” }}`
`{{ “aaabaab”
strings.Count “aa” }}`
`{{ “aaabaab”
strings.Count “aaa” }}`
`{{ “aaabaab”
strings.Count "" }}`
FirstUpper
strings.FirstUpper STRING
:字符串首字母大写
1
{{ strings . FirstUpper "foo" }} → "Foo"
HasSuffix
strings.HasSuffix STRING SUFFIX
:字符串是否含指定后缀
1
2
3
{{ $pdfPath := "/path/to/some.pdf" }}
{{ strings . HasSuffix $pdfPath "pdf" }} → true
{{ strings . HasSuffix $pdfPath "txt" }} → false
Repeat
strings.Repeat COUNT INPUT
:指定字符串重复多次,拼接返回
1
2
{{ strings . Repeat 3 "yo" }} → "yoyoyo"
{{ "yo" | strings . Repeat 3 }} → "yoyoyo"
RuneCount
strings.RuneCount INPUT
:字符串转 Go 的 rune
类型切片
TrimLeft
where
内置变量
站点变量
页面变量
菜单变量