Hugo的布局
了解Layouts是如何帮助您设置和重复使用 Hugo 网站的主要结构。
善于利用Layouts
是创建一个易于维护、灵活和繁荣的Hugo网站的关键,因此,让我们看看它们是如何发挥作用的。
什么是布局(Layouts)?
Layouts
是用于页面上围绕内容的所有 “框架”。例如,想想一个网站上不同页面之间保持相对不变的所有东西,如页眉和页脚。
在Hugo中,网站上的每个页面都是一个内容文件。最上面是一小段元数据,叫做front matter
,后面是markdown
。内容文件的目标是,以其最纯粹的形式存储内容。在内容文件中很少有任何HTML或其他表现逻辑。
所有用于显示和格式化内容的花哨的HTML都存在于布局中。一个布局可能被用于多个内容页面。例如,在一个项目组合网站上,你可能会对你的“关于”页和服务页面使用相同的布局。其他时候,特别是如果你正在做一些复杂的事情,你可能会有一个布局专门用于一个单独的内容页面,例如一个带有复杂表格的联系页面。
你的第一页 Your first page
Hugo有一个叫做 Page Bundles 的概念,可能很难让你头脑清醒。我们将在这里做最低限度的工作,以使您的网站正常运行。
默认情况下,内容页使用一种叫做 single
(单页)的布局。创建一个确切命名为_index.md
的内容文件,包括下划线和所有内容,这是一个特例。(Creating a content file which is named exactly , underscore and all, is the exception to this. )
这些页面就像它周围页面的目录,默认使用一种叫做 list
(列表)的布局。
就像一本好的非小说类书籍一样,从目录开始是一个很好的做法。您将为网站的主页执行此操作。
如果这一切听起来令人困惑,那么它实际上比听起来简单得多。在\content
目录下创建一个名为_index.md
的文件:hugo new content\_index.md
---
title: Home
---
Hello, I'm a ferocious lion.
---
是之前提到的元数据或 FrontMatter
。我们将在今后的课程中讨论这个问题。
您的第一个布局 Your first layout
Hugo 有一个层次结构来确定用于content_d文件的布局。最后的退路是在 _default
目录中寻找一个布局。(The last fallback is looking for a layout in the directory.)让我们为主页创建默认列表布局。
在 /layouts/
目录中创建一个新的文件夹,名为 _default
,在里面创建 list.html
,内容如下:
<!Doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{ .Page.Title }}</title>
</head>
<body>
{{ .Content }}
</body>
</html>
这些 {{ *** }}
部分是使用Hugo模板从我们的内容文件(_index.md
)中渲染内容,我们很快会介绍。
好了, 这是在你的Hugo网站上通过布局呈现的真实内容。让我们用你网站上的第二个页面来庆祝一下。
您的第二个布局
让我们在你的网站上弄第二个页面。创建:/content/about.md
(注意,我们在这里没有使用下划线)
---
title: About
---
I'm learning Hugo, one step at a time.
about.md
默认会寻找一个名为single
的布局。
我们可以把/layouts/_default/list.html
克隆到/layouts/_default/single.html
中,这样就可以正常工作了,但你将不得不维护同一个布局的两个非常相似的版本。我们希望减少重复,并尽可能地使我们的生活更轻松。
相反,在/layouts/_default/baseof.html
创建一个新的布局,内容如下:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{ .Page.Title }}</title>
</head>
<body>
{{ block "main" . }}
{{ end }}
</body>
</html>
这与list
布局几乎相同,只有一个小的变化。我们现在没有输出{{ .Content }},而是有一个主 “块”。这个块是一个占位符,其他布局可以用它来只指定页面的那一部分。
让我们用你的列表布局来做这件事。将/layouts/_default/list.html
里的内容替换为:
{{ define "main" }}
{{ .Content }}
{{ end }}
用完全相同的代码创建/layouts/_default/single.html
。
试着进入http://localhost:1313/about/,看看你的新的关于页面。
添加样式
这是一门Hugo课程,所以我们不打算进入任何CSS。但是,您和我都同意默认浏览器样式还有很多不足之处。我不仅会给你一个样式表,它还会是一个 Sass 样式表,以便在您扩展网站时易于维护。
我们已经有一个叫做static
的目录,所有的静态资源都在这里。如果我们使用一个普通的CSS文件,这将是放置它的地方。我们使用的是Sass,它需要被处理成一个CSS文件。
在我们网站的根部创建一个名为 /assets/
的目录。这是需要某种形式的处理的资源将住在这里。在assets
中,创建一个名为sass
的目录。这是我们的.scss
文件的位置。让我们在/assets/sass/main.scss
创建一个主文件,内容如下:
/* 设置 body 元素的宽度、外边距和字体 */
body {
width: 400px; /* 设置宽度为 400 像素 */
margin: 0 auto; /* 上下外边距为 0,左右外边距为自动,居中对齐 */
font-family: sans-serif; /* 设置字体为无衬线字体 */
}
/* 设置导航栏中的 ul 元素的样式 */
nav ul {
list-style: none; /* 移除列表项的默认样式 */
padding: 3px 5px; /* 设置内边距,上下 3 像素,左右 5 像素 */
background: #111; /* 设置背景颜色为深灰色 */
/* 设置导航栏中的 a 元素的样式 */
a {
color: #fff; /* 设置文字颜色为白色 */
text-decoration: none; /* 移除下划线 */
/* 设置鼠标悬停时的样式 */
&:hover {
text-decoration: underline; /* 添加下划线 */
}
}
/* 设置导航栏中的 li 元素的样式 */
li {
display: inline; /* 设置为行内元素 */
}
}
/* 设置 footer 元素的样式 */
footer {
background: #f2f2f2; /* 设置背景颜色为浅灰色 */
padding: 2px 2px; /* 设置内边距,上下左右均为 2 像素 */
font-size: .7em; /* 设置字体大小为 0.7 倍的默认字体大小 */
text-align: center; /* 设置文字居中对齐 */
}
/* 设置 id 为 map 的元素的高度 */
#map {
height: 300px; /* 设置高度为 300 像素 */
}
我们还需要在我们的HTML中引用样式表。打开/layouts/_default/baseof.html
,在</title>
下面插入以下内容:
{{ $style := resources.Get "sass/main.scss" | resources.ToCSS | resources.Minify }}
<link rel="stylesheet" href="{{ $style.Permalink }}">
这告诉Hugo建立.scss
文件并在一个样式表标签中输出链接。
你会注意到这将适用于 list
和 single
布局。我们之前所做的工作已经派上用场了! 你可能需要重新启动你的 hugo serve
以便在你的浏览器中看到这些变化。
下一步是什么?
目前,访问 “关于” 页面的唯一方法是直接导航到它。在下一课,我们将使用Hugo局部添加网站导航。
Hugo 的局部模板 Hugo partials
在这个Hugo教程中,学习如何用Partials
将你的Hugo页面分解成更小的 “组件”。
在本课中,我们将在网站的顶部创建一个导航栏,这个导航栏对所有页面都是一样的,我们将使用一个局部来实现。
Partial
的想法很简单:它是一个可以包含在布局中的文件,以减少重复或简单地隐藏一些复杂性。你将用一个局部模板来为你的网站添加一个导航条。虽然你可以把这个逻辑直接添加到你的 baseof.html
中,但有时把一个布局分割成较小的局部是很好的,这样你就不需要处理一个2000行的文件了。
你的第一个Partial
在你的layouts
目录下创建一个partials
文件夹,所以最终路径将是/layouts/partials/
。在里面创建nav.html
,内容如下:
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about/">About</a></li>
</ul>
</nav>
这就是已经处理好的导航。现在是时候把它纳入你的布局中了。打开/layouts/_default/baseof.html
,在<body>
下面添加以下内容:
{{ partial "nav.html" }}
在Go中,你的单引号对字符串没有好处,只有双引号才能指定一个字符串。
渲染你的页面,就可以了。
你的第二个Partial
让我们尝试另一个场景来展示Partial的力量。
我们将进一步简化 baseof.html
,把 <head>
的内容移到一个Partial
。创建 /layouts/partials/meta.html
,内容如下:
<meta charset="utf-8">
<title>{{ print .Page.Title }}</title>
{{ $style := resources.Get "sass/main.scss" | resources.ToCSS | resources.Minify }}
<link rel="stylesheet" href="{{ $style.Permalink }}">
在这个部分,我们有一些变量需要当前页面的上下文。幸运的是,传递当前页面的上下文可以用一个字符来完成。
打开/layouts/_default/baseof.html
,用以下内容替换<head>
的内容。
{{ partial "meta.html" . }}
结尾处的那个小 .
是传递当前页面的上下文,它允许部分打印出当前页面的标题。你会经常在你的Hugo网站中看到这种情况。
hugo模板的基础知识
模板化
Go模板是Hugo中使用的灵活的模板语言。在这一课中,我们将了解Go模板的基本概念,并看看你如何在你的网站上使用它。
通过Hugo模板,你可以控制你的页面是如何呈现的。你可以使用变量,在数组上循环,检查条件,并运行函数。把它看作是一种简单的编程语言,以帮助建立你网站上的页面。你布局中的那些大括号{{ }},就是Hugo模板。
什么是front matter
Font matter 是你的内容文件顶部的一个元数据片段。有些元数据将是专门为Hugo而设的。例如,设置一个布局,或表明当前页面是一个草稿。其他形式的元数据将是针对你的网站的,例如,表明在页面上使用哪种类型的英雄,或你最喜欢的五种食物的列表。
Front matter 的内容是以一个小的YAML片段的形式出现在内容文件的顶部。我们在索引页和关于页上都看到过这种形式。
它可能看起来不大,但我们可以在我们的布局中使用Hugo的模板来引用这个front matter。
什么是Hugo模板化?
Hugo使用Go模板作为其布局中的模板语言。一旦你掌握了它,它就很容易了。与Hugo中的许多事情一样,有时展示比讲述更容易。
Hugo模板化的例子
输出一个字符串
<!-- Go模板是一个普通的HTML页面。当你想执行一段代码时,你可以使用双大括号,像这样:-->
<p>A Go template is a normal HTML page. When you want to execute a piece of code, you can use double curly braces like this: {{ "Hello!" }}.</p>
Front matter的输出
<!-- You can reference a variable from your front matter in a layout with .Params. For example, you could output the title on your pages with:
你可以在布局中用.Params来引用你Front matter的一个变量。例如,你可以在你的页面上输出标题用:-->
<title>{{ .Params.title }}</title>
站点配置的输出
<!-- Sometimes you'll want to set a variable globally in your config.toml.
Hugo has already initalized a title in your config.toml. You can access
a variable from your global config with site. For example:
有时你会想在config.toml中设置一个全局变量。
Hugo已经在你的config.toml中设置了一个标题。你可以从你的全局配置中用site.访问一个变量。比如说:
-->
<title>{{ .Params.title }} | {{ .Site.title }}</title>
条件
<!-- We might want to check if the front matter title exists. If
it exists, output it; if not, use the global config title.
我们可能想检查一下前面的标题是否存在。如果它存在,就输出它;如果不存在,就使用全局配置的标题。-->
{{ if isset .Params "title" }}
<title>{{ .Params.title }}</title>
{{ else }}
<title>{{ .Site.title }}</title>
{{ end }}
设置并输出一个变量
<!-- variables at set with a $ sign. For example:
变量在设置时带有$符号。例如:-->
{{ $favorite_food := "Gazelle" }}
{{ $favorite_food }}
循环
<!--In Go, an array that can change size is called a slice.
You can iterate over an array or slice using range.
在Go中,一个可以改变大小的数组被称为切片。
你可以用range来遍历一个数组或切片。-->
{{ $best_friends := slice "pumbaa" "timon" "nala" "rafiki" }}
<ul>
{{ range $best_friends }}
<li>{{ . }}</li>
{{ end }}
</ul>
Nested Key values 嵌套键值
内容文件:
---
title: Appearance
apperance:
eyes: green
snoot: boopable
whiskers: true
limbs:
- claws: 5
side: left
position: front
- claws: 4
side: right
position: front
- claws: 3
side: left
position: back
- claws: 5
side: right
position: back
---
Layouts file:
<!-- If we want to output all of these variables, we could call .Params.appearance.x for each one. Instead we could use `with` to change the context to '.'. It also has the benefit of checking whether the variable exists and won't run the block if it doesn't.
如果我们想输出所有这些变量,我们可以为每个变量调用.Params.appearance.x。相反,我们可以使用`with`来改变上下文为'.'。它还有一个好处,就是检查变量是否存在,如果不存在,就不会运行这个块。
-->
{{ with .Params.appearance }}
<h3>My top appearance traits</h3>
<dl>
<dt>Eyes</dt>
<dd>{{ .eyes }}</dd>
<dt>Snoot</dt>
<dd>{{ .snoot }}</dd>
<dt>Whiskers</dt>
<dd>{{ .whiskers }}</dd>
{{ with .limbs }}
<dt>Claws</dt>
<dd>
<ul>
{{ range . }}
<li>{{ .position }} {{ .side }} {{ .claws }</li>
{{ end }}
</ul>
</dd>
{{ end }}
</dl>
{{ end }}
这些是模板化的基础。在你的Hugo之旅中,你将会使用所有这些概念。你可以浏览一下模板文档,了解一下你还能做什么。
如果你喜欢保持HTML输出的整洁,你可以使用{{-</code>和<code>-}}
来修剪标签周围的空白。Hugo文档中就有一个很好的例子。
把它全部放在一起
让我们把我们新的Hugo模板知识付诸行动,为你的网站添加一个包括你的名字和当前年份的页脚。在此基础上,我们将添加一个可选的前台事项字段,你可以用它来隐藏特定页面上的页脚。
让我们从一个简单的问题开始。你的名字。在你的config.toml
中把它作为一个新的键加入。因为这只是这个网站的东西,而不是一个特殊的Hugo术语,我们把它放在params对象下:
[params]
name = 'Simba'
现在让我们来创建局部。将footer.html
添加到你的layout partials
目录,内容如下:
{{ with .Params.hide_footer }}
<!-- No footer here! 这里没有页脚! -->
{{ else }}
<footer>
Website made by {{ .Site.Params.name }} in {{ now.Year }}
</footer>
{{ end }}
最后在baseof.html
布局中的</body>
之前调用局部:
{{ partial "footer.html" . }}
为了检查hide_footer
前台的工作情况,让我们通过在前台添加这个来关闭about.md
页面上的页脚:
hide_footer: true
运行hugo service
,在浏览器中查看你的网站。主页有页脚,而 “关于 “页却没有。
在Hugo写博客 Blogging in Hugo
在这个Hugo教程中,你将学习如何用Hugo的内容和布局创建一个博客。
Hugo中的博客相对来说是比较简单的。它包括一个列出所有博客文章的页面,以及一系列带有文章日期的内容页面。这就是它的全部内容。
创建一个博客列表页
你将在这个例子中看到布局分层是如何工作的。
在你的/content/
目录下创建一个名为post
的目录,并在其中创建一个名为_index.md
的文件,内容如下:
title: Blog
_index.md
- 还记得这意味着什么吗?它是一个内容文件,在这种情况下,它将列出你的文章。目前,它将尝试使用/layouts/_default/list.html
布局。你不希望使用这个布局,因为这个页面没有任何内容。相反,你要创建一个新的布局,专门用于列出文章。
Hugo布局层次的工作方式是,它将首先寻找一个与当前章节相匹配的布局(将章节视为目录),然后再回到_default
中的全局默认值。换句话说,我们可以在layouts
目录下创建一个名为post
的新目录,并在里面创建list.html
,内容如下:
{{ define "main" }}
<h1>My posts</h1>
<ul>
{{ range .Pages }}
<li>
<a href="{{ .Permalink }}">{{ .Title }}</a> - {{ .Date.Format "January 2, 2006" }}
</li>
{{ end }}
</ul>
{{ end }}
这里有一些新的概念;让我解释一下。
一个列表页(_index.html
)有一个包含其所有子页的数组,变量为.Pages
。
.Date
的格式被调用,并被传递一个2006年的随机日期。为什么会这样呢?这是 Go 的一个格式化日期的怪癖。你可以在这里阅读更多关于它的信息。
.Permalink
可以在任何页面上调用,以获得其末端的 URL。如果你想链接到一个页面,它就特别有用。
这就是我们的列表页所需要的一切。让我们转到一篇文章上。
创建一个帖子 我们已经完成了最困难的部分。让我们通过创建一些帖子来完成这个博客。
帖子住在/content/posts
目录下,不需要任何特殊的命名规则。我喜欢鼓励的一个建议是在文件名中加入文章的日期。Hugo会完全忽略它,但当你在成百上千的文章中找到一篇文章时,它是有帮助的。
让我们创建三篇博文,让你开始吧。
/contents/posts/2022-04-03-i-like-to-roar.md
---
title: I like to roar
date: 2022-04-03
---
Hi, quick update from me. I just want to let everyone know that I like roaring.
/contents/posts/2022-04-02-today-i-made-friends.md
---
title: Today I made friends
date: 2022-04-02
---
I got lost in the woods today. Two friends, a Meerkat and a Warthog, found me,sung a song with me and gave my life new meaning. Today was a good day.
/contents/posts/2022-04-01-vegan-experiment.md
---
title: Vegan experiment
date: 2022-04-01
---
I tried to become a vegan today. I made it to lunch time and couldn't bear the sight of another green leaf. Yuck!
就像博客列表页一样,这些帖子将尝试使用/layouts/_default/single.html
。让我们在/layouts/posts/single.html
创建一个专门用于文章的布局,内容如下:
{{ define "main" }}
<h1>{{ .Params.Title }}</h1>
<p>{{ .Date.Format "January 2, 2006" }}</p>
{{ .Content }}
{{ end }}
最后,让我们把博客添加到导航中。打开/layouts/partials/nav.html
,添加另一个项目:
<li><a href="/posts/">Blog</a></li>
运行hugo serve
,在浏览器中打开网站,查看你的杰作。
Using Data with Hugo
使用数据文件导入现有数据,或者在这个Hugo教程中拥有管理全局数据的简单方法。
Hugo中的数据文件有点像一个只读的数据库。它的工作方式是你可以把JSON、CSV、YAML、XML或TOML文件放在一个叫做data
的目录中,然后用.Site.Data
在布局中访问这些数据。如果你想花哨一点,你也可以在构建时从外部来源下载JSON或CSV文件。
创建你的第一个数据文件
首先,我们要创建一个包含所有你喜欢的度假地点的数据文件。创建/data/vacation_spots.yml
,内容如下:
- name: Masai Mara National Reserve
latitude: -1.484751
longitude: 35.101904
- name: Serengeti National Park
latitude: -2.333333
longitude: 34.833332
- name: Okavango Delta
latitude: -19.304543
longitude: 22.643703
- name: Etosha National Park
latitude: -18.855591
longitude: 16.32932
- name: Kidepo Valley Park
latitude: 3.882778
longitude: 33.874444
什么是短码?
为了列出你最喜欢的度假地点,你需要:
- 地图软件–你将使用神奇的OpenStreetMap和Leaflet。
- 一个标记物的JavaScript列表–你目前在一个数据文件中拥有这个列表
- 用于设置地图和标记的JavaScript
- 一个容纳地图的div
我们可以把所有这些放在一个内容文件中,但这将比我们目前拥有的简单的markdown要复杂得多。如果我们想在另一个页面上再次使用这个地图呢?那就需要复制和粘贴大量的HTML了。
幸运的是,Hugo有一个答案,它叫做Shortcodes(短码)。短码类似于部分代码,只是你在内容文件中使用它们。Hugo甚至已经内置了一堆。
你将创建一个短码,可以在任何内容文件中使用,以创建一个带有标记位置的地图。让我们看看它是如何工作的。
你的第一个短码
在/layouts/
中创建一个名为shortcodes
的目录,并添加一个名为 vacation_spots.html
的文件,内容如下:
<div id="map">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" crossorigin=""></script>
<script>
let markers = {{ $.Site.Data.vacation_spots }};
</script>
<script src="/map.js"></script>
</div>
这里有几件事情,让我解释一下:
首先,我们有一个地图元素,它将容纳地图。
然后我们加载Leaflet的样式表,它将帮助我们在地图上创建漂亮的图钉和弹出式窗口。
接下来是Leaflet的JavaScript文件。
接下来是来自你的数据文件的标记物列表。这里它被输出为一个JSON数组。
最后我们参考/map.js
,我们很快就会创建。它负责初始化地图和添加标记。
让我们添加JavaScript来初始化地图和添加标记。创建/static/map.js
,内容如下:
const map = L.map('map');
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'})
.addTo(map);
let bounds = [];
for (let i = 0; i < markers.length; i++ ) {
const marker = L.marker([markers[i].latitude, markers[i].longitude]).addTo(map);
marker.bindPopup(markers[i].name);
bounds.push([markers[i].latitude, markers[i].longitude]);
}
map.fitBounds(bounds);
我们不是来学习JavaScript的,所以我把这个留给你来解读(或不解读)。
下一步是什么?
这只是你Hugo之旅的开始。你现在已经掌握了建立一个基本的Hugo网站的技能。要继续这个旅程,我推荐一些资源:
Hugo文档是学习Hugo所提供的一切的伟大资源。
Hugo社区论坛对新来者很欢迎。这是一个获得支持和与社区联系的好地方。
Bootstrap到客户可编辑的Hugo网站是一个更高级的Hugo教程,它告诉你如何将一个纯粹的静态网站转换成客户可更新的Hugo网站。
谢谢你的阅读,祝你Hugo快乐
参考链接:
https://cloudcannon.com/community/learn/hugo-beginner-tutorial