Hugo 不完美教程 - V: Templates 其它模板

前言

shot.jpg

本静态站点用于演示之用,使用 Hugo 构建,以及 Markdown 供应内容。

流行的静态站点框架有以下几个:

  • Jekyll (基于 Ruby 容易上手) https://www.jekyll.com.cn/docs/home/
  • Hexo (基于 Node.js 容易上手) https://hexo.io/docs/
  • Hugo (基于 Go) https://gohugo.io/documentation/

演示站点有两个访问入口:

  • https://jimbowhy.gitee.io/
  • https://jimboyeah.github.io/

此站点提供了一篇关于 Hugo 静态站点生成框架的入门教程。

  • Hugo 不完美教程 - IX: Menus 菜单组织
  • Hugo 不完美教程 - VIII: Functions 内置函数
  • Hugo 不完美教程 - VII: Variables 对象变量
  • Hugo 不完美教程 - VI: Multilingual 多语言支持
  • Hugo 不完美教程 - V: Templates 模板机制
  • Hugo 不完美教程 - V: Templates 其它模板
  • Hugo 不完美教程 - IV: Hugo Pipes 管道处理
  • Hugo 不完美教程 - III: Hugo Modules 模块
  • Hugo 不完美教程 - II: Hugo 目录组织
  • Hugo 不完美教程 - I: Hugo Web Framework

代码仓库地址如下,查看 hugo-project 分支是原文件,master 分支是发布的静态站点文件:

  • https://github.com/jimboyeah/jimboyeah.github.io/tree/hugo-project
  • https://gitee.com/jimbowhy/jimbowhy/tree/hugo-project/
    shot.jpg

title: "V: Templates 其它模板"
description: "坚果的 Hugo 教程"
date: 2020-08-06T20:14:08-04:00
featured_image_: "/assets/IMG_20190117_123248_s1.jpg"
summary: Hugo 直接使用了 Golang 的模板语法,表达能力很强大,配合 Hugo 预定义变量或自定义变量实现非常强大的静态站点功能。Mardown 文件提共内容数据,而模板则是数据的消化系统。这部分介绍其它各种模板的使用。
tags: ["hugo", "menu"]


目录:

[TOC]

Homepage Template 主页模板

根据不同的内容,对应的不同的主页面,下面是其中的 Home 和 JSON 的主页定位方式,这里并没有列出主题目录,因为主题的 layouts 目录结构意义是一样的:

  {
    "Example": "Home page",
    "Kind": "home",
    "OutputFormat": "HTML",
    "Suffix": "html",
    "Template Lookup Order": [
      "layouts/index.html.html",
      "layouts/home.html.html",
      "layouts/list.html.html",
      "layouts/index.html",
      "layouts/home.html",
      "layouts/list.html",
      "layouts/_default/index.html.html",
      "layouts/_default/home.html.html",
      "layouts/_default/list.html.html",
      "layouts/_default/index.html",
      "layouts/_default/home.html",
      "layouts/_default/list.html"
    ]
  },
  {
    "Example": "JSON home",
    "Kind": "home",
    "OutputFormat": "JSON",
    "Suffix": "json",
    "Template Lookup Order": [
      "layouts/index.json.json",
      "layouts/home.json.json",
      "layouts/list.json.json",
      "layouts/index.json",
      "layouts/home.json",
      "layouts/list.json",
      "layouts/_default/index.json.json",
      "layouts/_default/home.json.json",
      "layouts/_default/list.json.json",
      "layouts/_default/index.json",
      "layouts/_default/home.json",
      "layouts/_default/list.json"
    ]
  },

一个主页面模板参考:

{{ define "main" }}
    

{{.Title}}

{{ with .Params.subtitle }} {{.}} {{ end }}
{{.Content}}
{{ range first 10 .Site.RegularPages }} {{ .Render "summary"}} {{ end }}
{{ end }}

shortcode 短代码模板

短代码模板 shortcode 用来生成定义一段功能代码,供内容文件调用生成页面。

Hugo 提供了几个内置的 shortcode,可是在国内网络环境却不太好用:

  • figure 使用语法及对应生成

      {{}}
    
      

    Steve Francia

  • gist 用于生成 Git 版本仓库中的 URL

      {{}}
    
      https://gist.github.com/spf13/7896402
    
      {{}}
    
      
    
  • param 获取页面扉页参数

    假设,设置了页面扉页参数 testparam: Hugo Rocks! 那么就可以通过将其输出到页面:

      {{}}
    
      {{}}
    
  • refrelref 根据页面文件生成相对或绝对页面引用 URL

      [Neat]({{< ref "blog/neat.md" >}})
      [Who]({{< relref "about.md#who" >}})
    
      Neat
      Who
    
  • highlight 生成高亮代码块

  • instagram 生成 Instagram 图片 URL,国内墙

      {{}}
    
      https://www.instagram.com/p/BWNjjyYFxVx/
    
  • tweet 国内墙

  • vimeo 国内墙

  • youtube 国内墙

这些被墙的应用可能会导致 Hugo 运行失败,因为网站访问超时导致,如 twitter.html 定义使用了 api.twitter.com

{{- $pc := .Page.Site.Config.Privacy.Twitter -}}
{{- if not $pc.Disable -}}
{{- if $pc.Simple -}}
{{ template "_internal/shortcodes/twitter_simple.html" . }}
{{- else -}}
{{- $url := printf "https://api.twitter.com/1/statuses/oembed.json?id=%v&dnt=%t" (index .Params 0) $pc.EnableDNT -}}
{{- $json := getJSON $url -}}
{{ $json.html | safeHTML }}
{{- end -}}
{{- end -}}

可以将有问题 API 调用注解掉,Hugo Docs 项目就使用了它们:

{{/*< instagram_simple BGvuInzyFAe hidecaption >*/}}
{{/*< twitter_simple 1085870671291310081 >*/}}
{{/*< youtube ZJthWmvUzzc >*/}}
{{/*< vimeo_simple 48912912 >*/}}

另外,对于 MD 生成的标准的 HTML 标签,像表格,或列表,无法直接在 MD 设置样式:

{{}}
| Key | Value |
|---|---|
| Static Site Generator | Hugo |
| Language | Go |
{{}}

那么可以通过定义 shortcodes 的方式来加外层 DIV 通过 CSS 级联样式去定义,也可以使用

{{ $table := .Inner | markdownify | safeHTML }}
{{ $tableBulma := $table | replaceRE "" "
" }} {{ $tableBulma | safeHTML }}

用户定义短代码模板的定位按以下顺序:

  • /layouts/shortcodes/.html
  • /themes//layouts/shortcodes/.html

先定义一个 shortcode 模板 layouts\shortcodes\page-kinds.html

所有页面 Page 对象都有一个 .Kind 属性变量,如下:

|    Kind    |          说明          |                              例子                              |
|------------|------------------------|----------------------------------------------------------------|
| `home`     | 正在加载的是首页       | `/index.html`                                                  |
| `page`     | 正在加载的指定页面     | `my-post` page (`/posts/my-post/index.html`)                   |
| `section`  | 正在加载的是分区页     | `posts` section (`/posts/index.html`)                          |
| `taxonomy` | 正在加载的是分类页     | `tags` taxonomy (`/tags/index.html`)                           |
| `term`     | 正在加载的是分类术语页 | term `awesome` in `tags` taxonomy (`/tags/awesome/index.html`) |

然后在 MD 内容页面中调用 shortcode 以生成对应的内容:

{{ page-kinds %}}
    sometext
{{ /page-kinds %}}

shortcode 短代码模板可使用的属性变量,以上面的调用方法作为参考,对应值如下:

{{

}}

属性 说明 参考值
.Name Shortcode 名字 page-kinds
.Ordinal 基于 0 的序号,表示 shortcode 在页面内容的位置 1
.Parent 嵌套的 parent shortcode
.Position 所在页面文件名和行列号,常用于调试 "C:\quickstart\content\posts\2nd-post.md:29:5"
.IsNamedParams 指示是否使用命名参数,而不是位置化参数 false
.Inner 在 shortcode 标签之间的的内容 sometext

{{}}

Hugo 官方文档项目中提供了很好的 shortcode 模板学习示例,例如,最常用来展示高亮代码片段 code 为例,当你在查看官方文档 MD 文件时,看到以下这样的内容:

{{}}
...
{{}}

这就表示,MD 文件正使用 code 生成相应的内容,来看看它的定义 layouts_default\shortcodes\code.html

{{}} {{ codeLang := "" }}
{{ " suffix }}
{{ codeLang = . }}{{ end }}
{{ if eq codeLang = "go-html-template" }}
{{ end }}

{{.}}

{{- end -}}

    {{ if ne (.Get "copy") "false" }}
        
        {{/* Functionality located within filesaver.js The copy here is located in the css with .copy class so it can be replaced with JS on success */}}
    {{end}}
    
{{ if .Get "nocode" }}{{ $.Inner }}{{ else }}{{ with $codeLang }}{{- highlight $.Inner . "" | -}}{{ else }}
{{- .Inner | string -}}
{{ end }}{{ end }}

{{}}

这里最主要的是 highlight 这个内置函数,对代码片断进行处理得到高亮效果。也可以直接使用 highlight 像以下这样输出带行高的代码高亮代码块:

{{}}
{{/>}}
// GetTitleFunc returns a func that can be used to transform a string to
// title case.
//
// The supported styles are
//
// - "Go" (strings.Title)
// - "AP" (see https://www.apstylebook.com/)
// - "Chicago" (see https://www.chicagomanualofstyle.org/home.html)
//
// If an unknown or empty style is provided, AP style is what you get.
func GetTitleFunc(style string) func(s string) string {
switch strings.ToLower(style) {
case "go":
return strings.Title
case "chicago":
return transform.NewTitleConverter(transform.ChicagoStyle)
default:
return transform.NewTitleConverter(transform.APStyle)
}
}
{{ / highlight */>}}
{{
}}

又以 note 短代码模板为例,它在文档页面中用来生成注解信息块:

{{ $_hugo_config := `{ "version": 1 }` }}

首先,模板中对 _hugo_config 变量进行赋值,但没有使用它。然后生成一个 note-icon 图标,然后再将注解内容 .Inner 输出到 div.admonition-content 标签上。

其中,注释掉的部分没用起作用,但是这里调用了几个 Hugo 函数,urlize 用来将一个字符串进行 URL 合法化编码,参考 urlize.md 文档。

注意,{{- .Inner -}} 模板中的负号用来移除内容的前缀、后缀空格。

如果内部包含 Markdown 内容,需要使用 % 分隔符:

{{ tab name="Tab 1" %}}This is **markdown**{{ /tab %}}

Partials 模板

Partials 片断模板用来引用片断内容到页面中显示。

模板文件定位:

  • layouts/partials/*.html
  • themes//layouts/partials/*.html

Hugo 工程的所有片断模板都位 layouts/partials 这一个目录,可以下设多个子目录,参考如下:

.
└── layouts
    └── partials
        ├── footer
        │   ├── scripts.html
        │   └── site-footer.html
        ├── head
        │   ├── favicons.html
        │   ├── metadata.html
        │   ├── prerender.html
        │   └── twitter.html
        └── header
            ├── site-header.html
            └── site-nav.html

然后,在页面中引用上面这些片断模板,参考语法格式:

{{ partial "/.html" . }}

后面的点号表示将当前的上下文数据对象传入去。

可以将 partial 模板当作自定义函数来使用,参考 Ananke 的 func/GetFeaturedImage.html:

{{}} {{/*
GetFeaturedImage
@return Permalink to featured image, or an empty string if not found.
*/}}

{{ $linkToCover := "" }}

{{/* Use the value from front matter if present */}}
{{ if .Params.featured_image }}
    {{ $linkToCover = .Params.featured_image }}

{{/* Find the first image with 'cover' in the name in this page bundle. */}}
{{ else }}
    {{ $img := (.Resources.ByType "image").GetMatch "*cover*" }}
    {{ with $img }}
        {{ $linkToCover = .Permalink }}
    {{ end }}
{{ end }}

{{/* return either a permalink, or an empty string. Note that partials can only have a single
return statement, so this needs to be at the end of the partial (and not in the if block) */}}
{{ return $linkToCover }}

{{}}

然后,在需要使用的模板中调用:

{{ $featured_image := partial "func/GetFeaturedImage.html" . }}

Pagination 分页模板

分页也可以当作菜单一样使用,在列表页面、Sessions、taxonomies 中进行分页呈现条目,让静态站点看下来更像是动态的站点。

配合 SQL-like 一样的操作函数 first, last, after 可以对条目进行排序。

在 config.toml 配置分页,设置每一页显示的条目数量和分页参数在 URL 中的 path 显示配置,默认样式 /page/1/

Paginate = 10
PaginatePath = page

分页将 homepage, sections, taxonomies 页面数据分成一小块加载显示,有两种方式使用 .Paginator 生成分布标签:

  • 在模板中直接调用 .Paginator.Pages
  • 调用分页函数和排序再生成分页标签;

示例:

{{ .Paginate.Pages }}
{{ range (.Paginate ( first 50 .Pages.ByTitle )).Pages }}

配置文件中的 Paginate 可以在模板中覆盖,最后一个参数指定:

{{ range (.Paginator 5).Pages }}
{{ $paginator := .Paginate (where .Pages "Type" "posts") 5 }}

还可以使用 GroupBy 函数进行分组:

{{ range (.Paginate (.Pages.GroupByDate "2006")).PageGroups  }}

分布模板的最简单方法就是引入内置的分页模板 pagination.html,它兼容 Bootstrap 样式:

{{ $paginator := .Paginate (where .Pages "Type" "posts") }}
{{ template "_internal/pagination.html" . }}
{{ range $paginator.Pages }}
   {{ .Title }}
{{ end }}

参考 .Paginator 对象提个的数据属性:

{{

}}

属性 说明
PageNumber The current page’s number in the pager sequence
URL The relative URL to the current pager
Pages The pages in the current pager
NumberOfElements The number of elements on this page
HasPrev Whether there are page(s) before the current
Prev The pager for the previous page
HasNext Whether there are page(s) after the current
Next The pager for the next page
First The pager for the first page
Last The pager for the last page
Pagers A list of pagers that can be used to build a pagination menu
PageSize Size of each pager
TotalPages The number of pages in the paginator
TotalNumberOfElements The number of elements on all pages in this paginator

{{}}

页面对像提供了大量变量,如 .Title, .Permalink 等,具体参考 page.md 文档。

Hugo 内置分页模板定义如下:

{{}} {{ .Paginator }}
{{ if gt pag.First -}}




  • {{ end -}}
  • {{- pag.Pagers -}}
    {{- showNumber := or (eq .TotalPages 5) (le .PageNumber 3) (eq showNumber -}}
    {{- shouldEllipse = false -}}
    {{- else -}}
    {{- ellipsed -}}
    {{- showNumber }}



  • {{- end -}}
    {{- end }}



  • {{- end }}

    {{ end }}
    {{
    }}

    Menu Templates 菜单模板

    定义一个 layouts/partials/sidebar.html

    {{}}


    {{
    }}

    内置函数 absLangURLrelLangURL 可以正确处理多语言配置的站点,此外,可以使用 absURL 和 relURL,它们会将语言前缀在 URL 上。

    在配置文件中设置 Section Menu 以将分区页面做为菜单数据:

    sectionPagesMenu = "main"
    

    然后在内页中呈现:

    
    

    配置站点菜单 config.toml:

    [menu]
    
      [[menu.main]]
        identifier = "blog"
        name = "This is the blog section"
        title = "blog section"
        url = "/blog/"
        weight = -110
    

    或者,在页面中定义菜单数据:

    ---
    title: Menu Templates
    linktitle: Menu Templates
    menu:
      docs:
        title: "how to use menus in templates"
        parent: "templates"
        weight: 130
    ---
    ...
    

    在菜单中使用 .Page 获取页面对象:

    
    

    Single Page Template

    内容页面类型指定为 page,那么就会有相应的 page variables 和 site variables,在它们的模板文件中可以使用这些变量:

    具体变量列表需要查阅 pages.md 和 site.md。

    模板定位,参考 docs.json 给出的数据部分数据,如果页面在 posts 目录下就适用以下定位:

    {
    "Example": "Single page in \"posts\" section",
    "Kind": "page",
    "OutputFormat": "HTML",
    "Suffix": "html",
    "Template Lookup Order": [
      "layouts/posts/single.html.html",
      "layouts/posts/single.html",
      "layouts/_default/single.html.html",
      "layouts/_default/single.html"
    ]
    },
    

    那么,在建立一个单页面模板 layouts/posts/single.html 就会有效:

    {{}} {{ define "main" }}


    {{ .Title }}




    {{ .Content }}




    {{ end }}
    {{
    }}

    List Templates 列表模板

    参考 Hugo 文档的函数列表部分实现,在函数索引列表页面中,将各个函数 MD 内容中的 linktitle 和 description 呈现在页面上,例如 content\en\functions\md5.md 内容中扉页数据定义如下:

    ---
    title: md5
    linktitle: md5
    description: hashes the given input and returns its MD5 checksum.
    godocref:
    date: 2017-02-01
    publishdate: 2017-02-01
    lastmod: 2017-02-01
    categories: [functions]
    menu:
      docs:
        parent: "functions"
    keywords: []
    signature: ["md5 INPUT"]
    workson: []
    hugoversion:
    relatedfuncs: [sha]
    deprecated: false
    aliases: []
    ---
    

    Hugo 文档使用的主题是模块方式导入的,见 config.toml 配置文件:

    [module]
      [module.hugoVersion]
        min = "0.56.0"
      [[module.imports]]
        path = "github.com/gohugoio/gohugoioTheme"
    

    _vendor 目录下的 gohugoioTheme 定义了一个 layouts\partials\docs\functions-signature.html 片断,它会在每个函数页面模板中呈现函数的语法内容:

    {{ if isset .Params "signature" -}}
      {{- with .Params.signature }}
        

    Syntax

    {{- range . }}
            {{- . -}}
          
    {{- end }} {{- end -}} {{ end }} {{/* The inline style overrides `pre` styling defaults */}}

    然后在页面 section 模板中 layouts/_default/page.html 使用它来呈现函数语法信息:

    {{- partial "docs/functions-signature.html" . -}} {{ .Content }}

    你可能感兴趣的:(Hugo 不完美教程 - V: Templates 其它模板)