Hugo

komantao 2019-12-11 2020-03-09 字数 47981 阅读量


Hugo是使用go(又称golang)语言编写的一个静态网站生成器,主要用于构建Blog、项目文档、静态网站等。以其极快的速度、强大的内容管理和强大模板语言而著称,不仅解决了环境依赖、性能较差的问题,还有简单、易用、高效、易扩展、快速部署等诸多优点,开发过程中使用LiveReload(即时渲染,即时预览网站效果),极大优化了文章写作体验。

“ 静态”的含义反映在网站页面的生成时间。一般web服务器(WordPress、Ghost、Drupal等)在收到页面请求时,需要调用数据库生成页面,再返回给用户请求。静态网站在建立网站前就生成所有页面(称为静态文件),访问时直接返回现成的静态页面,不需要数据库参与。

一、概述

静态网站基本不需要维护,不需要考虑复杂的运行时间、依赖、数据库和安全性等问题。静态网站最大好处是访问快速。当网站有更改时,静态网站生成器需要重新生成所有与更改相关的页面。对于小型的个人网站、项目主页等,网站规模很小,重新生成整个网站也非常快。Hugo在速度方面做得非常好,官方宣传每篇页面的生成时间不到1ms。

如果说速度快是Hugo的第一大优点,那么安装简单应该就是Hugo的第二大优点。

Hexo基于node.js开发,需要依赖环境才能运行,要实现一些主题额外功能还需要下载和匹配各种依赖,甚是麻烦。Hugo基于go开发,打包成独立文件,不需要额外环境,安装并设置系统的环境变量后,就能直接运行,可以说是非常便利和快捷。

1、安装

示例:Windows平台安装Hugo。

  1. Hugo Releases下载对应平台的Hugo二进制文件

    image-20191127114555468

  2. 本地创建一个目录存储Hugo的可执行文件和项目文件

    在本地创建一个新目录:示例为E:/Hugo

    • Hugo目录中创建一个子目录(E:/Hugo/bin):存储可执行文件
    • Hugo目录中创建另一个子目录(E:/Hugo/sites):存储Hugo项目文件
  3. 下载的zip包解压到E:/Hugo/bin文件夹内,就完成了安装

  4. 设置环境变量,将E:/Hugo/bin添加到系统变量中的环境变量Path中

  5. 安装好后,运行hugo version检测是否成功返回版本号

    $ hugo version
    Hugo Static Site Generator v0.61.0-9B445B9D windows/amd64 BuildDate: 2019-12-11T08:29:13Z
    

升级时:将新版本的zip包解压覆盖E:/Hugo/bin文件夹内的文件。

2、命令

语法:

hugo [command] [flags]

command的参数(可使用hugo --help命令查看其详细帮助文档)有:

command 功能
config 输出(显示)站点配置
convert 转换内容(content)的格式
deploy 将站点部署到云提供商
env 输出(显示)Hugo版本和环境信息
gen 几个有用的生成器的集合
help 关于任何命令的帮助
import 从其他站点导入您的站点
list 列出内容的各种类型
mod Hugo模块助手
new 为您的站点创建新内容
server 一个高性能的网络服务器(调用本地web服务器)
version 输出(显示)Hugo的版本号

2.1 创建内容文件

在项目根目录(又称网站根目录,示例为Hugo/sites/ebook)中运行Hugo命令。

  • 新建网站(Hugo项目)或主题

    hugo new [command]
    

    参数:

    command 功能 示例 备注
    site 新建项目 hugo new site ebook 项目以目录形式存在,目录名任意
    theme 新建主题
  • 网站内新建内容(称为页面文件)

    hugo new [path] [flags]
    

    根据提供的路径(path参数)创建一个新的内容文件,根据原型文件自动设置日期和标题等。原型文件使用“-k|–kind”参数指定或由Hugo智能猜测。

    参数 描述 示例 备注
    path 内容文件在content目录中的相对路径 hugo new hugo/hugo.md 文件路径:content/hugo/hugo.md
    flags 使用“-k|–kind”指定内容文件的类型 hugo new --kind chapter hugo/_index.md 文件类型指定为chapter

2.2 编译内容文件

  • 本地预览

    编译生成缓存的静态文件(不会输出到public目录)并启动本地web服务:

    $ hugo server
    Building sites …
    …………                         
    Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
    Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
    Press Ctrl+C to stop
    

    一直保持运行命令。手动结束命令后,将会清除缓存的静态文件。

  • 生成网站文件

    生成静态文件并输出到默认的public目录(不会启动本地web服务):

    $ hugo
    Building sites …
                       | EN
    +------------------+-----+
      Pages            |  47
      Paginator pages  |   0
      Non-page files   |  47
      Static files     | 211
      Processed images |   0
      Aliases          |  11
      Sitemaps         |   1
      Cleaned          |   0
      
    Total in 897 ms
    

3、项目

3.1 、新建项目

CLI(命令行工具,例如cmd、PowerShell或Bash等)进入E:/Hugo/sites目录:

E:\>cd E:\Hugo\sites
E:\Hugo\sites>

E:/Hugo/sites目录下新建项目(假设名称为ebook):

$ hugo new site ebook
Congratulations! Your new Hugo site is created in E:\Hugo\sites\ebook.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
   create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>\<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.
  • 提示信息:下载主题、添加内容、运行hugo server命令
  • Hugo没有预设主题,安装Hugo后需要下载主题

3.2 、项目结构

项目目录的树结构为:

.                    # Hugo项目根目录(示例为E:\Hugo\sites\ebook)
├─archetypes         # markdown文件的模板,通常用来自定义设置扉页等
| └─default.md       # 缺省,Hugo预设的一个模板
├─content            # 内容目录,存放网站内所有内容文件的源代码
| ├─_index.md        # 网站的主页(网站的简介)
| ├─章节文件夹        # 章节目录,内容文件分类存放
| | ├─_index.md      # 章节的主页(章节的简介)
| | ├─内容文件夹      # 章节内的子页面目录(根据需要设置)
| |   ├─xxx.md       # 子页面内容,通常是markdown文件
| |   └─本地图片      # markdown文件引用的图片 
├─data               # 网站的自定义配置文件,文件类型可以是yaml|toml|json等格式
├─layouts            # 布局目录,存放渲染content目录的模版文件,模版的文件类型是html格式
├─resource           # 主题产生的目录,未明用途
├─static             # 静态资源目录,存放静态资源文件,例如:图片、css、js等
├─themes             # 主题目录,存放Hugo主题
| └─XXX主题          # 安装后的主题名称
| | └─layouts        # 某个主题下的网站模板文件
├─public             # 编译后生成的网站文件
└─config.toml        # 网站的配置文件(支持格式有:toml、yaml、json)
  • content目录:存放网站内所有页面文件的源代码
  • data目录:存放网站的自定义配置文件
  • static目录:存放源代码的静态资源文件(图片、css、js等)。渲染时,会直接将static目录的文件直接复制到public目录,不做任何渲染
  • 指定主题:在config.toml中使用theme = "xxx"设置默认主题(xxx是theme目录内的主题名), 或运行hugo|hugo server --theme=xxx命令时设置临时渲染主题
  • 优先级:若存在同名文件,Hugo优先使用项目根目录内的文件,然后才是themes目录内的指定主题

4、Hugo的坑

虽然只是一些小问题,但每次重复发生,也是一个麻烦。

  1. Hugo的Date属性带有时区

    Hugo日期格式:date: 2019-12-11T23:19:14+08:00;Hexo日期格式:date: 2019-11-25 18:49:20。在迁移时注意此问题并进行格式转换。

  2. Hugo的网址链接

    若直接输入网址(示例:https://gohugo.io/),Hugo不能识别为网址;

    若粗体显示网址(示例:https://gohugo.io/),Hugo能识别为网址。

  3. Hugo默认使用“Pretty URLs”(漂亮URL),Hexo使用“Ugly URLs”(丑陋URL)

    ├─content           # 网站源代码目录
    | ├─_index.md       # URL转换为:https://example.com/
    | ├─posts           # 一个名为posts的章节文件夹
    | | ├─_index.md     # URL转换为:https://example.com/posts/
    | | ├─first.md      # URL转换为:https://example.com/posts/first/
    | | └─topic         # 章节内的子页面内容目录(包含md文件和图片)
    | | | ├─second.md   # URL转换为:https://example.com/posts/topic/second/
    | | | └─123.jpg     # second.md文件引用的本地图片
    

    “_index.md”和”index.md”文件在Hugo中具有特殊含义(用于组织页面)。在“Pretty URLs”渲染下,与实际路径一致。

    若文件名不为“_index.md”或“index.md”时,“Pretty URLs”将md文件渲染为目录。导致后果:md文件引用本地图片时,渲染路径比实际路径多了一个与文件名同名的目录名,导致引用图片失败。

  4. Hugo_v0.60的Markdown渲染引擎使用Goldmark,Typora使用GFM’s spec

    1. 代码(<code>标签)/代码块(<pre>标签)内包含html或js语法时

      • Typora

        Typora内的代码块会屏蔽所有语法,只显示源代码。

      • Hugo(Goldmark)

        config.toml中若设置了unsafe = true,将会允许md文件使用html和js语法,执行html或js语句的优先级高于代码块,结果执行代码块内的html语句,导致格式错乱。因此,必须慎重开启unsafe。

    2. 在列表项中新建块/段落时

      Typora成功(参阅严格模式,使用Enter键代替输入空格),Hugo有时失败。

      • 原因一:Hugo的config.toml开启了unsafe = true

      • 原因二:列表项中有代码块(<pre>标签)

        Hugo有时渲染成功,有时渲染失败,暂未明原因。

        解决方法:貌似只要一次性完成列表项就能成功,即不要在现有列表项中插入新的块/段落。若需插入,应该是重写列表项内的所有内容。

      • 原因三:列表项中的代码块的扉页语法貌似不能使用yaml语法

        在md文件的扉页使用了yaml语法的前提下:

        • 若在列表项中的代码块的扉页语法同样使用yaml语法,会造成格式错乱,解决方法是改用toml语法
        • 若代码块不在列表项中时,扉页语法可正常使用yaml语法
    3. 标题锚点的语法

      • Typora

        只能实现页面内跳转(简捷语法:<a href="#标题路径">xx</a>)、页面跳转(Markdown语法:[替换文本](文件相对路径))。

      • Hugo

        Hugo生成的每一个html文件和html文件内的每一个标题在网络上都有唯一的URL,可实现页面内(简捷语法:<a href="#标题网址">xx</a>)、页面、页面间跳转,建议统一使用Markdown语法:[替换文本](标题网址))。

二、主题

Hugo通过主题将md文件渲染为HTML格式,主题实质是HTML/JavaScript/CSS的集合:

  • HTML:构造页面的标签元素
  • CSS:配置标签元素的样式
  • JavaScript:设置标签元素的动作

若想自定义(修改)主题,首先定位html文件了解主题的架构、使用浏览器的开发者工具(F12)了解标签元素和其样式,然后遵循此页面新建一个HTML/JavaScript/CSS文件(直接修改主题代码是一个坏习惯):

  • 主题的预设部件(themes/hugo-theme-learn/layouts/partials目录):

    layouts/partials目录内自定义一个同名文件(优先级高于主题预设的部件)。

  • 主题的预设CSS(themes/hugo-theme-learn/static/css目录):

    static/css目录内自定义一个css文件(优先级高于主题预设CSS)。

1、安装主题

Hugo没有预设主题,可在Hugo的官方网站(或此GitHub仓库)将目标主题下载(克隆)到Hugo项目根目录内的themes目录中。

git clone 主题的git地址 themes/新名称
  1. 进入themes目录(E:/Hugo/sites/ebook/themes

    E:\Hugo\sites\ebook>cd themes
    
  2. 安装主题

    推荐:learndocDock(基于learn的移植,最大特色是幻灯片式播放md文件)。

    $ git clone https://github.com/matcornic/hugo-theme-learn.git
    Cloning into 'hugo-theme-learn'...
    remote: Enumerating objects: 2249, done.
       
    Receiving objects: 100% (2249/2249), 13.40 MiB | 11.00 KiB/s, done.
    Resolving deltas: 100% (1224/1224), done.
    

2、learn主题

learn主题的官方文档: https://learn.netlify.com/

learn主题结构:

  • 网站配置文件:配置网站的基本参数

    themes/hugo-theme-learn/exampleSite/config.toml覆盖项目根目录内的config.toml后,进行自定义修改。

  • 网站布局文件:构造网站的HTML结构

    themes/hugo-theme-learn/layouts/index.html,文件内调用其它html文件、Hugo的页面变量等。

  • 网站样式文件:渲染HTML标签元素的样式

    themes/hugo-theme-learn/static/css,在html文件中引用CSS文件。使用浏览器的开发者工具查看HTML标签元素来源于哪一个CSS文件。

  • 原型文件:内容文件的模板,主要是配置扉页等

    themes/hugo-theme-learn/archetypes,learn主题预设了2个原型文件:

    • chapter.md:构建章节文件(type:chapter)

      将其放入archetypes目录内,作为一个通用模板,方便其它主题调用。

    • default.md:构建文件(type:所有类型)

2.1 网站配置文件

Hugo的网站配置文件(config.toml)配置网站的整体设置,其初始默认设置非常简单:

baseURL = "http://example.org/"       # 网站URL,运行本地web服务器时不需要此值
languageCode = "en-us"                # 网站语言
title = "My New Hugo Site"            # 网站标题

config.toml自定义修改后的结果:

baseURL = "http://example.org/"
DefaultContentLanguage = "zh-cn"
title = "无边风月"
theme = "hugo-theme-learn"

[outputs]
home = [ "HTML", "RSS", "JSON"]

[[menu.shortcuts]]
  name = "<i class='fas fa-user'></i> 简介"
  url = "/about/"
  weight = 11

[[menu.shortcuts]]
  name = "<i class='fas fa-align-justify'></i> 分类"
  url = "/categories/"
  weight = 12

[[menu.shortcuts]]
  name = "<i class='fas fa-bookmark'></i> 标签"
  url = "/tags/"
  weight = 13

[markup]
  [markup.tableOfContents]
    endLevel = 4
    startLevel = 2
  [markup.goldmark.renderer]
    unsafe = true
	
[params]
  themeVariant = "mine"
  author = "komantao"
  ordersectionsby = "weight"
2.1.1 默认语言

根据主题的语言包(themes/hugo-theme-learn/i18n)内的文件名称配置网站语言:

languageCode = "en-us"      DefaultContentLanguage = "zh-cn"
2.1.2 params参数
[params]
  editURL = ""                           # 在每一个页面的右上角位置添加“Edit this page”按钮
  author = ""                            # 网站作者名称
  description = ""                       # 网站描述
  showVisitedLinks = false               # 值为true时,菜单栏显示已访问的复选标记
  disableSearch = false                  # 值为true时,禁用搜索功能,将隐藏搜索栏
  disableAssetsBusting = false           # 值为false时,生成新网站后自动清除Javascript and CSS的缓存
  disableInlineCopyToClipBoard = false   # 值为false时,允许内联代码的“复制到剪贴板”按钮
  disableShortcutsTitle = false          # 值为false时,菜单中快捷键的标题默认设置(等于文章标题)
  disableLanguageSwitchingButton = false # 值为true时,使用多语言网站时,禁用切换语言按钮
  disableBreadcrumb = true               # 值为true时,隐藏面包屑导航(指页面顶栏的导航),只显示当前页标题
  disableNextPrev = true                 # 隐藏“下一页”和“上一页”按钮
  ordersectionsby = "weight"             # 在菜单中按“weight”或“title”排序。默认为“weight”
  themeVariant = ""                      # 配色方案。可以是“red”、“blue”、“green”或自定义
2.1.3 markup参数

Hugo v_0.60的Markdown渲染库改用Goldmark(放弃了Blackfriday)。

默认情况下,Goldmark禁止原始HTML和潜在危险的链接。若Markdown文件内有内联HTML或JavaScript,则需要在config.toml内开启unsafe:

[markup]
  [markup.goldmark.renderer]
    unsafe = false  # 缺省值为false,值为true时,md文件可使用html和js语法
  • 慎重开启unsafe选项!!!

    因为Hugo会执行代码块内的HTML源代码,造成版面格式错乱或其它莫名其妙的问题。

配置TOC目录的层级:

[markup]
  [markup.tableOfContents]
    endLevel = 4
    startLevel = 1
  • startLevel:设置标题标签的开始层级,在TOC目录内显示的最小层级是H2
    • 若取值为1,在HTML页面构建H1的标签;但不会在TOC目录内显示H1
    • 若取值大于1,在HTML页面不构建指定层级之前的标签
  • endLevel:设置标题标签的结束层级,但在TOC目录内显示的最大层级是H4
    • 若取值小于4,在HTML页面不构建指定层级之后的标签
    • 若取值大于4,根据实际在HTML页面构建标签;但不会在TOC目录内显示H5

配置语法高亮(highlight):

[markup]
  [markup.highlight]
    codeFences = true
    hl_Lines = ""
    lineNoStart = 1
    lineNos = false
    lineNumbersInTable = true
    noClasses = true
    style = "monokai"
    tabWidth = 4

由于learn主题在header.htmlfooter.html文件中自定义了语法高亮(本地引用highlight.js)插件,覆盖了Hugo的语法高亮。

2.1.4 搜索功能
[outputs]
home = [ "HTML", "RSS", "JSON"]

learn主题将使用hugo 20+版本中的最新改进来生成json索引文件,以供lunr.js javascript搜索引擎使用。

Hugo在public文件夹的根目录下生成lunrjs index.json,构建网站(hugo|hugo server)时,在内部生成它(不会显示在文件系统中)。

2.1.5 菜单项

在菜单(menu)中定义其他菜单条目(menu.main)或快捷方式(menu.shortcuts)。

在网站配置文件config.toml中添加快捷方式:

[[menu.shortcuts]]
name = "<i class='fab fa-github'></i> Github repo"
identifier = "ds"
url = "https://github.com/matcornic/hugo-theme-learn"   # 使用远程仓库地址
weight = 10
[[menu.shortcuts]]
  name = "<i class='fas fa-tags'></i> 标签"
  url = "/tags"                                         # 使用本地地址:tags属性
  weight = 11
[[menu.shortcuts]]
  name = "<i class='fas fa-camera'></i> 关于"
  url = "/about.html"                                   # 使用本地地址:自定义属性
  weight = 11
  • url参数值的来源:

    • 使用远程仓库网址

    • 使用扉页中的属性值,例如:tags属性、categories属性

      1. 在Markdown文件的扉页中设置tag属性,标签按其插入顺序显示在页面的顶部

        +++
        date = 2018-11-29T08:41:44+01:00
        title = Theme tutorial
        weight = 15
        tags = ["tutorial", "theme"] 
        +++
        
      2. 在menu中显示标签

        [[menu.shortcuts]]
        name = "<i class='fas fa-tags'></i> Tags"
        url = "/tags"
        weight = 30
        
    • 自定义本地内容文件(示例:关于about)

      1. content根目录内新建一个about.md文件(不需要创建子目录)

      2. config.toml文件中添加快捷方式,自定义name和url

        可以通过更改本地的i18n目录内的语言包配置文件来覆盖(翻译)标题(name)。

        示例:在i18n/en.toml文件,添加以下内容

        [Shortcuts-Title]
        other = "<Your value>"
        

        若设置了disableShortcutsTitle=true,则此标题(name)被禁用。

  • url参数值的写法由uglyurls参数决定:

    • uglyurls = true:丑陋URLs,url应该写为url = "/about.html"
    • uglyurls = false:漂亮URLs,url应该写为url = "/about"

2.2 HTML

learn主题预设的html文件存放路径:themes/hugo-theme-learn/layouts

2.2.1 header

内容页面的header标签(包含导航),不会被覆盖。

themes/hugo-theme-learn/layouts/partials/header.html文件:

  1. 在内容文件中添加以下信息:

    image-20191214131536049

    懒得新建html文件,直接添加:

    1. <head>标签内添加busuanzi的js插件

      <head>
          <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js">         </script>
      
    2. <div id="body-inner">标签内添加一个<p>标签:

      <div id="body-inner">
        {{if and (not .IsHome) (not .Params.chapter)}}
        <br>
        <h1>{{.Title}}</h1>
        {{ if not .Data.Terms }}
          <p class="authorline">
             {{with .Params.author}}
                 <i class='fa fa-user'></i>  <a href="https://github.com/username/username.github.io">{{ . }}</a>
             {{end}}
               <span> <i class='fa fa-calendar'></i> {{.Date.Format "2006-01-02"}}</span>
             <span> <i class="fa fa-wrench" aria-hidden="true"></i> {{.Lastmod.Format "2006-01-02"}}</span>
             <span> 字数 {{.WordCount}}</span>
             <span id="busuanzi_container_page_pv"> 阅读量 <span id="busuanzi_value_page_pv"></span>次</span>
            </p>
          <br>
        {{end}}  
        {{end}}
      
  2. 自定义语法高亮

    通过CDN(BootCDNstaticfile.org)在线引用Highlight.js插件。

    备注:若Highlight.js插件无法识别编程语言,将无法着色。此时,应该不选择语言或选择近似语言。

    <head>标签内添加:

    <link href="https://cdn.bootcss.com/highlight.js/9.15.10/styles/zenburn.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/highlight.js/9.15.10/highlight.min.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>
    
    • 第一行:在线引用配色方案monokai.min.css,learn主题本地使用(themes/hugo-theme-learn/static/css/atom-one-dark-reasonable.css),调用文件themes/hugo-theme-learn/layouts/partials/header.html

    • 第二行:在线引用highlight.js压缩版,learn主题本地使用(themes/hugo-theme-learn/static/js/highlight.pack.js),调用文件themes/hugo-theme-learn/layouts/partials/footer.html

    • 第三行:调用highlight.js进行着色(语法高亮)

    因此,若想更改learn主题的语法高亮,只需要修改第一行,即将

    <link href="{{"css/atom-one-dark-reasonable.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
    

    修改为:

    <link href="https://cdn.bootcss.com/highlight.js/9.15.10/styles/zenburn.min.css" rel="stylesheet">
    
  3. 代码块添加行号

    highlight.js插件本身没有显示行号的功能,百度搜索教程自行添加行号:

    themes/hugo-theme-learn/layouts/partials/footer.html的body标签内的末尾位置添加:

    <script type="text/javascript">
        var e = document.querySelectorAll("pre code");
        var i;
        for (i = 0; i < e.length; i++) {
         e[i].innerHTML = "<ul><li>" + e[i].innerHTML.replace(/\n/g, "\n</li><li>") + "\n</li></ul>";
         $("code ul li").last().remove();
        }
    </script>
    
    • 将代码块(pre)内的代码(code)的换行符\n替换为标签<ul><li>
    • 使用 $("code ul li").last().remove();删除多增加的一行空行

    在CSS文件(static/scc/theme-mine.css)内添加:

    /* highlight.js插件的语法高亮  */
    .hljs ul {
        list-style: decimal;
        margin: 0 0 0 40px!important;
        padding: 0
    }
    .hljs li {
        list-style: decimal-leading-zero;
        border-left: 1px solid #111!important;
        padding: 2px 5px!important;
        margin: 0!important;
        line-height: 14px;
        width: 100%;
        box-sizing: border-box
    }
    .hljs li:nth-of-type(even) {
        background-color: rgba(255,255,255,.015);
        color: inherit
    }
    
    • list-style: decimal;是根据<li>标签自动编号
  4. 增加“back to top”按钮

    <div id="body-inner">标签内添加一个<p>标签和js代码:

    <p id="back-to-top"><i class="fa fa-space-shuttle fa-2x fa-rotate-270" aria-hidden="true"></i></p>
    <script>
          var backButton=$('#back-to-top');
          backButton.hide();      /*首先将#back-to-top隐藏*/
       function backToTop() {
            $('html,body').animate({scrollTop: 0}, 800);
       return false;
          }
          backButton.on('click', backToTop);
          $(window).on('scroll', function () {/*当滚动条的垂直位置大于浏览器所能看到的页面的那部分的高度时,回到顶部按钮就显示 */
            if ($(window).scrollTop() > $(window).height())
                backButton.fadeIn();
            else
                backButton.fadeOut();
          });
          $(window).trigger('scroll');/*触发滚动事件,避免刷新的时候显示回到顶部按钮*/
    </script>
    

    然后在CSS文件中设置样式:

    /* 回到顶部按钮 */
    p#back-to-top{
        position:fixed;
        bottom:6%;
        right:3%;
    }
    p#back-to-top i{
        color:blue;
        display:block;
        z-index: -100;
    }
    p#back-to-top i:hover{
        color:red;
    }
    
2.2.2 favicon

网站图标(在浏览器的标题内显示)。

layouts/partials目录内新建一个文件,命名favicon.html。然后编写HTML。使用<link>标签并引用在static文件夹内的图像(假设在static/images目录内)。

<link rel="shortcut icon" href="/images/favicon.png" type="image/x-icon" />

网站徽标(在页面左上角显示)。

layouts/partials目录内新建一个文件,命名logo.html。然后编写想要的任何HTML。使用img标签并引用在static文件夹内的图像(假设在static目录内)。

<a id="logo" href="{{ .Site.BaseURL }}">
  <img class="custom-logo" src="夜空.jpg">
</a>

左侧菜单的页脚。

<p>Theme:<a href="https://getgrav.org">Grav</a> and <a href="https://gohugo.io/">Hugo</a></p>

2.3 CSS

learn主题预设的CSS文件存放路径:themes/hugo-theme-learn/static/css

预设了3种CSS配色方案(red、blue、green),直接在config.toml修改参数即可使用:

[params]
  themeVariant = "green"

自定义CSS配色方案:在static/css目录内新建一个CSS文件(文件名使用theme前缀,例如theme-mine.css)。

:root{
    /* body标签:文章正文 */
    --MAIN-TEXT-color:#323232; /* 缺省颜色 */
    --MAIN-TITLES-TEXT-color: #5e5e5e; /* h2-h3-h4-h5标题的颜色 */
    --MAIN-LINK-color:#1C90F3; /* a标签:链接(links)的颜色 */
    --MAIN-LINK-HOVER-color:#DC143C; /* 悬停在链接时的颜色 */
    --MAIN-ANCHOR-color: #1C90F3; /* color of anchors on titles */

    /* nav标签:导航栏(或称侧边栏、菜单栏) */
	/* #sidebar #header-wrapper:导航栏内的header,包括网站图标和搜索栏  */
	--MENU-HEADER-BG-color:#FFFFF0; /* 导航栏内的header的背景颜色 */
    --MENU-HEADER-BORDER-color:#33a1ff; /* 导航栏内的header的边框颜色 */ 
	--MENU-WIDTH:15em; /* 导航栏相对宽度 */
    
	/* #sidebar .searchbox:导航栏内的搜索栏  */
    --MENU-SEARCH-BG-color:#D3D3D3; /* 搜索栏的背景颜色 */
    --MENU-SEARCH-BOX-color: #33a1ff; /* 搜索栏的边框颜色 */
    --MENU-SEARCH-BOX-ICONS-color: #000000; /* 搜索栏中的图标的颜色 */

    /* #sidebar ul.topics > li.parent, #sidebar ul.topics > li.active:活动组件,指被选中的章节或其内容  */
    --MENU-SECTIONS-BG-color:#E0FFFF; /* 导航栏的缺省颜色 */
    --MENU-SECTIONS-LINK-color: #000000; /* 导航栏内链接的颜色 */
    --MENU-SECTIONS-LINK-HOVER-color: #FF0000;  /* 导航栏内链接悬停时的颜色 */
	--MENU-SECTIONS-ACTIVE-BG-color:#D3D3D3; /* 活动组件(active section,被选中时)的背景颜色,包含子组件 */
    --MENU-SECTION-ACTIVE-CATEGORY-color: #777; /* 活动组件的颜色 */
    --MENU-SECTION-ACTIVE-CATEGORY-BG-color: #FDF5E6; /* 活动组件的背景颜色 */

    --MENU-VISITED-color: #33a1ff; /* Color of 'page visited' icons in menu */
    --MENU-SECTION-HR-color: #20272b; /* Color of <hr> separator in menu */
    
}

body {
    color: var(--MAIN-TEXT-color) !important;
}

#body .padding {
	padding: 15px 1rem;
}

#sidebar {
    width: var(--MENU-WIDTH) !important;
}
#body {
    margin-left: var(--MENU-WIDTH) !important;
}

textarea:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="password"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="text"]:focus, input[type="url"]:focus, input[type="color"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, select[multiple=multiple]:focus {
    border-color: none;
    box-shadow: none;
}

h1 {
	background: var(--MENU-SECTIONS-BG-color);
	font-weight: 700;
}

#chapter h1 {
	border-bottom: none;
}

#chapter p {
	text-align: left;
}
h2, h3, h4, h5 {
    color: var(--MAIN-TITLES-TEXT-color) !important;
}

h2 {
	font-size: 1.8em;
	margin: 20px 0;
	padding-left: 0px;
	font-weight: 700;
	line-height: 1.4;
	background: var(--MENU-HEADER-BORDER-color);
}
h3 {
	font-weight: 700;
	font-size: 1.2em;
	line-height: 1.4;
	margin: 10px 0 5px;
	padding-top: 10px;
}
h4 {
	font-weight: 700;
	/* text-transform: uppercase;              控制文本的大小写:uppercase 大写 */
	font-size: 1.1em;
	line-height: 1.4;
	margin: 10px 0 5px;
	padding-top: 10px
}
h5, h6 {
	font-size: 1em;
}

a {
    color: var(--MAIN-LINK-color);
}

.anchor {
    color: var(--MAIN-ANCHOR-color);
}

a:hover {
    color: var(--MAIN-LINK-HOVER-color);
}

#sidebar ul li.visited > a .read-icon {
	color: var(--MENU-VISITED-color);
}

#body a.highlight:after {
    display: block;
    content: "";
    height: 1px;
    width: 0%;
    -webkit-transition: width 0.5s ease;
    -moz-transition: width 0.5s ease;
    -ms-transition: width 0.5s ease;
    transition: width 0.5s ease;
    background-color: var(--MAIN-LINK-HOVER-color);
}
#sidebar {
	background-color: var(--MENU-SECTIONS-BG-color);
}
#sidebar #header-wrapper {
    background: var(--MENU-HEADER-BG-color);
    color: var(--MENU-SEARCH-BOX-color);
    border-color: var(--MENU-HEADER-BORDER-color);
}
#sidebar .searchbox {
	border-color: var(--MENU-SEARCH-BOX-color);
    background: var(--MENU-SEARCH-BG-color);
}

#sidebar ul.topics > li.parent, #sidebar ul.topics > li.active {
    background: var(--MENU-SECTIONS-ACTIVE-BG-color);
}
#sidebar .searchbox * {
    color: var(--MENU-SEARCH-BOX-ICONS-color);
}

#sidebar a {
    color: var(--MENU-SECTIONS-LINK-color);
	font-weight: 400;
}

#sidebar a:hover {
    color: var(--MENU-SECTIONS-LINK-HOVER-color) !important;
}

#sidebar ul li.active > a {
    background: var(--MENU-SECTION-ACTIVE-CATEGORY-BG-color);
    color: var(--MENU-SECTION-ACTIVE-CATEGORY-color) !important;
}

#sidebar hr {
    border-color: var(--MENU-SECTION-HR-color);
}

#sidebar ul.topics ul a {
	color: #1E90FF;
}

#sidebar section#shortcuts {
	background: #DDA0DD;
}

#body #navigation {
	display: none;
}

/* 设置TOC  */
#TableOfContents > ul > li > a {
	padding: 0;
	color: #000000;
}
#TableOfContents > ul > li > ul > li > a {
	padding: 0 20px;
}
#TableOfContents > ul > li > ul > li > ul > li > a {
	padding: 0 25px;
}

/* 设置代码块  */
#body-inner pre {
	max-height: 300px;
    overflow: auto;
	padding: 0;
    margin: 0;
	white-space: pre;	
}

#body img, #body .video-container {
	margin: 1rem auto;
}

/* 设置表格  */
table th {
	background: #999999;
	text-align: center;
}
table th, table td {
  border: 1px solid #000000;
  padding: 0.2rem;
}
/* 设置引用  */
blockquote {
	background: #F5F5F5;
	border-left: 4px solid #0066FF;
}

自定义配色方案后,在config.toml中配置参数:

[params]
  themeVariant = "mine"

2.4 图标和徽标

Hugo-theme-learn主题已加载了Font Awesome库,可以轻松显示Font Awesome中的免费图标(icon)或徽标(logo)。

浏览旧版本的Font Awesome库(v5.12不能预览图标)选择好图标(例如heart)后,复制其HTML后粘贴到Markdown文件中:

<i class="fas fa-heart"></i>
  • docDock主题默认内置font-awesome.min.css(不支持fas图标)
  • learn主题默认内置fontawesome-all.min.css(支持fas图标)

3、docDock主题

Hugo-theme-docDock主题教程:

Hugo-theme-docDock主题结构:

  • 网站配置文件:配置网站的基本参数

    themes/hugo-theme-docdock/exampleSite/config.toml覆盖项目根目录内的config.toml后,进行自定义修改。

  • 网站页面布局文件:构建页面的HTML结构

    路径:themes/hugo-theme-docdock/layouts

    • 页面文件模板:_default/baseof.html
    • 单体页面模板:_default/single.html,例如:构建md文件的页面
    • 列表页面模板:_default/list.html,例如:构建tags或categories的页面
    • 列表页面模板:_default/li.html,构建docDock主题的tags首页
    • 章节页面模板:index.html
  • 网站样式文件:渲染HTML标签元素的样式

    路径:themes/hugo-theme-docdock/static/css。html文件引用CSS文件,使用浏览器的开发者工具可查看HTML标签元素来源于哪一个CSS文件。

  • 原型文件:themes/hugo-theme-learn/archetypes

    learn主题预设了2个原型文件(生成内容文件的模板):

    • slide.md:构建章节文件(type:slide,幻灯片)
    • default.md:构建文件(type:所有类型)

docDock主题移植于learn主题,因此,可参考learn主题的配置。

3.1 网站配置文件

参考示例自定义修改config.toml为:

baseURL = "https://kuang.netlify.com/"   # 发布网站的URL
languageCode = "en"                      # 网站语言
defaultContentLanguage = "zh-cn"         # 没有语言指示符的内容将默认为这种语言
title = "风月"                           # 网站名称
theme = "hugo-theme-docdock"             # 启用主题
enableRobotsTXT = true                   # 生成robots.txt,告诉搜索引擎允许其抓取所有内容
enableEmoji = true                       # 启用表情符号
hasCJKLanguage = true                    # true时,自动检测内容中的中文/日语/韩语。这将使.Summary和.WordCount正确识别CJK语言
enableGitInfo = true                     # Lastmod项使用Git的最后提交时间
relativeURLs = true                      # URL相对于content目录
canonifyURLs = true                      # 将相对URL转换为绝对
paginate = 10                            # 分布(pagination)中每页的默认元素数量。但分页失败,建议填写一个较大值,避免渲染失败
PaginatePath = "page"
buildDrafts = false                      # 构建(build)时,true表示包含草稿(drafts)

[outputs]
home = [ "HTML", "RSS", "JSON"]

[[menu.shortcuts]]
    name = "<i class='fa fa-th'></i> 分类"
    url = "/categories"
    weight = 12

[[menu.shortcuts]]
    name = "<i class='fa fa-tags'></i> 标签"
    url = "/tags"
    weight = 13
	
[markup]
    [markup.tableOfContents]
        endLevel = 6                                  # TOC的标题结束层数(H6)
        startLevel = 1                                # TOC的标题开始层数(H1)
    [markup.goldmark.renderer]
        unsafe = false

[params]
    author = "komantao"                               # 作者
	authorsdescribe =  "一缕风尘空蒙 一泓月色潋滟"    # 作者描述
	description = "学习笔记"                          # 网站描述,方便SEO
	github = true                                     # 社交链接
	editURL = ""                                      # 顶栏显示“Edit this page”(输入content目录在源代码仓库内的URL)
    themeStyle = "original"                           # docDock主题预设有2种类型:"original"、"flex"(缺省值)
    themeVariant = "mine"                             # 主题变体(即CSS配色方案)预设值有:"green"、"gold"、"gray"、"blue"(缺省值),可自定义新的变体
    ordersectionsby = "weight"                        # 章节(sections)排序:weight(自定义排序权重值)、title(缺省值,按照文档的title排序)
    disableHomeIcon = false                           # true时,禁止显示网站主页图标;缺省值为false
    disableSearch = false                             # true时,禁止搜索;缺省值为false
    disableNavChevron = true                          # true时,隐藏上一页/下一页导航(next/prev chevron);缺省值为false
    highlightClientSide = true                        # true时,使用highlight.pack.js插件,从而代替hugo默认的chroma highlighter
    menushortcutsnewtab = true                        # true时,在新的标签页打开菜单项的链接(shortcuts links),但貌似无效;缺省值为false
	disableAssetsBusting = false
	copyrighthtml = "Copyright &#xA9; 2020 komantao. All Rights Reserved."      # 自定义版权信息
	enableGitalk = true

[params.reward]                                       # 设置打赏功能
    enable = true
    wechat = "/images/wechat.png"                     # 微信二维码

[params.gitalk]                                       # 使用Gitalk设置评论区
    clientID = "xxx"                                  # GitHub应用程序(Gitalk)生成的client ID
    clientSecret = "xxx"                              # GitHub应用程序(Gitalk)生成的client secret
    repo = "discuss"                                              # 存储评论(comments)的GitHub仓库名
    owner = "xxx"                                                 # GitHub仓库的所有者(owner)的GitHub ID
    admin= "xxx"                                                  # GitHub仓库的所有者和合作者(collaborators),指对仓库具有写访问权限的用户
    id= "location.pathname"                                       # 页面的唯一ID
    labels= "gitalk"                                              # Github的issue标签
    perPage= 15                                                   # 分页大小(Pagination size),最大为100
    pagerDirection= "last"                                        # 评论排序方向,可用值为'last'和'first'
    createIssueManually= true                                     # false时, 在评论仓库的issue内自动显示管理员登录信息
    distractionFreeMode= false                                    # false时,启用热键(cmd|ctrl + enter)提交评论
  • paginate

    Hugo_v0.6自动分页失败,建议将paginate值设置为一个较大值,避免渲染失败:

    ERROR 2020/02/14 11:27:41 failed to render pages: render of “taxonomyTerm” failed: “E:\Hugo\sites\book\themes\hugo-theme-docdock\layouts_default\list.html:7:42”: execute of template failed: template: _default\list.html:8:3: executing “main” at <partial “pagination.html” $paginator>: error calling partial: “E:\Hugo\sites\book\themes\hugo-theme-docdock\layouts\partials\pagination.html:7:42”: execute of template failed: template: partials/pagination.html:7:42: executing “partials/pagination.html” at <.Next.RelPermalink>: can't evaluate field RelPermalink in type *page.Pager

  • 启用highlight.js

    [params]
        highlightClientSide = true
    

    同时,禁用(注释或删除)pygments( 从0.30版本开始,被Chroma替代 )

    # pygmentsCodeFences = true
    # pygmentsStyle = "monokailight"
    

3.2 HTML

在导航栏内(<div id="header">标签)显示网站logo。

  1. 新建一个logo.html存入layouts/partials目录

    <a id="logo" href="{{ .Site.BaseURL }}">
        <img class="custom-logo" src="/images/风车.gif">  /* 网站logo图片存放在static/images目录内 */
    </a>
    
  2. 调用logo.html

    themes/hugo-theme-docdock/layouts/partials/original/body-beforecontent.html文件(<div id="header">标签)内调用logo.html

    <div id="header">
        {{ partial "logo.html" . }}
    
3.2.2 header

导航栏内(<div id="header">标签)显示作者、作者描述、网站描述和社交链接等。

  1. config.toml中设置选项开关

    [params]
        author = "komantao"                               # 作者
        authorsdescribe =  "一缕风尘空蒙 一泓月色潋滟"    # 作者描述
        description = "学习笔记"                          # 网站描述,方便SEO
        github = true                                     # 社交链接
    
  2. 添加HTML标签

    {{with .Site.Params.author}}                          /* 添加作者标签 */
        <a>{{ . }}</a>
    {{end}}
    <br/>
    {{with .Site.Params.authorsdescribe}}                 /* 添加作者描述标签 */
        <span style="white-space: pre;">{{ . }}</span>
    {{end}}
    <br/>
    {{with .Site.Params.github}}                          /* 添加社交标签 */
        <a href="https://github.com/xxx/xxx.github.io"><i class="fa fa-github" aria-hidden="true"></i></a>
    {{end}}
    
3.2.3 导航栏标题

在导航栏的列表(<ul class="topics">标签)内增加一个标记,显示为“目录”。

themes/hugo-theme-docdock/layouts/partials/original/body-beforecontent.html中的<ul class="topics">标签内新增一个标签:

<ul class="topics">
    <a>目录:</a>

显示效果:

image-20191217230519267

同理,在导航栏的<section id="shortcuts">标签内增加一个标题,显示为“其它”并修改为:

<section id="shortcuts">
    <a>其它:</a>
    {{- range sort . "Weight"}}
         <li class="" role="">
             {{- .Pre -}}
             <a href="{{.URL}}" target="_blank" rel="noopener">{{safeHTML .Name}}</a>
             {{- .Post -}}
          </li>
    {{- end}}
</section>
  • <a>标签内的target="_blank",表示新建页面打开链接。若删除,在原页面打开链接
3.2.4 Top Bar
3.2.4 标签页

docdock主题预设了一个tags首页,点击页面内的标签(区别于点击导航栏内的tags快捷键)弹出的首页,文件路径为:themes/hugo-theme-docdock/layouts/_default/li.html

3.2.5 文章信息

在文章开始位置增加以下信息:

image-20191214131536049

  1. 添加busuanzi的js插件

    themes/hugo-theme-docdock/layouts/partials/original/head.html文件中添加:

    <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js">         </script>
    
  2. 设置config.toml

    hasCJKLanguage = true              # 若为true,则自动检测内容中的中文/日语/韩语。这将使.Summary和.WordCount正确识别CJK语言
    
  3. 添加html标签

    themes/hugo-theme-docdock/layouts/partials/original/body-beforecontent.html文件中的<div id="body-inner">标签内添加一个<p>标签:

    <div id="body-inner">
        {{if and (not .IsHome) (not .Params.chapter)}}
            <br>
            <br>
            <h1>{{.Title}}</h1>
            {{ if not .Data.Terms }}
                <p class="authorline">
                    {{with .Params.author}}
                        <i class='fa fa-user'></i>  <a>{{ . }}</a>
                    {{end}}
                    <span> <i class='fa fa-calendar'></i> {{.Date.Format "2006-01-02"}}</span>
                    <span> <i class="fa fa-wrench" aria-hidden="true"></i> {{.Lastmod.Format "2006-01-02"}}</span>
                    <span> 字数 {{.WordCount}}</span>
                    <span id="busuanzi_container_page_pv"> 阅读量 <span id="busuanzi_value_page_pv"></span>次</span>
                </p>
                <br>
            {{end}}
        {{end}}
    
3.2.6 Highlight.js
  1. 开启Highlight.js

    docdock主题已在本地安装了Highlight.js插件,在themes/hugo-theme-docdock/layouts/partials/original/scripts.html文件中调用themes/hugo-theme-docdock/static/js/highlight.pack.js,但只使用了缺省的配色方案。

    config.toml中开启Highlight.js:

    [params]
        highlightClientSide = true
    

    或通过CDN(BootCDNstaticfile.org)在线引用Highlight.js插件。

    themes/hugo-theme-docdock/layouts/partials/original/head.html文件中添加:

    <link href="https://cdn.bootcss.com/highlight.js/9.15.10/styles/zenburn.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/highlight.js/9.15.10/highlight.min.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>
    
    • 第一行:在线引用zenburn.min.css配色方案

    • 第二行:在线引用highlight.js插件

    • 第三行:调用highlight.js进行着色(语法高亮)

  2. 着色(选择配色方案)

    若选择了Highlight.js插件不能识别的语言,将会着色失败,从而导致后面的添加行号也失败。解决方法是清空"选择语言"内容,Highlight.js将使用默认着色。

    若想更改配色方案,只需要修改第一行:

    <link href="https://cdn.bootcss.com/highlight.js/9.15.10/styles/zenburn.min.css" rel="stylesheet">
    
  3. 代码块添加行号

    highlight.js插件本身不能显示行号,百度搜索教程自行添加行号。

    themes/hugo-theme-docdock/layouts/partials/original/body-aftercontent.html文件的{{ partial "custom-content-footer.html" . }}后面添加:

    <script type="text/javascript">
        var e = document.querySelectorAll("pre code");
        var i;
        for (i = 0; i < e.length; i++) {
            e[i].innerHTML = "<ul><li>" + e[i].innerHTML.replace(/\n/g, "\n</li><li>") + "\n</li></ul>";
            $("code ul li").last().remove();
        }
    </script>
    
    • 将代码块(pre)内的代码(code)的换行符\n替换为标签<ul><li>
    • 使用 $("code ul li").last().remove();删除多增加的一行空行

    themes/hugo-theme-docdock/static/theme-original/variant-mine.css文件内添加:

    /* highlight.js插件的语法高亮  */
    .hljs ul {
        list-style: decimal;
        margin: 0 0 0 40px !important;
        padding: 0;
    }
    .hljs li {
        list-style: decimal-leading-zero;
        border-left: 1px solid #111!important;
        padding: 2px 5px!important;
        margin: 0!important;
        line-height: 14px;
        width: 100%;
        box-sizing: border-box;
    }
    .hljs li:nth-of-type(even) {
        background-color: rgba(255,255,255,.015);
        color: inherit;
    }
    .copy-to-clipboard {
        display: none;
    }
    pre .copy-to-clipboard {
        display: inline-block!important;
        top: 0;
        right: 0;
    }
    
    • list-style: decimal;是根据<li>标签自动编号
  4. 复制按钮

    docDock主题的代码块已设计了复制按钮(Copy to clipboard)。代码块(<pre code>标签)设置最大高度(添加滑条)和将复制按钮固定在<pre>标签内、<code>标签外(复制按钮不承滑条而滚动):

    /* 设置代码块  */
    #body-inner pre {
     background: black;
     padding: 1.5rem 0 0 0!important;    /* 增加顶部边距,为复制按钮预留空间 */
     margin: 0;
     white-space: pre;	
    }
    #body-inner pre code {
     max-height: 300px;         /* 设置最大高度 */
        overflow: auto;            /* 自动滚动 */
    }
    pre .copy-to-clipboard {
     display: inline-block!important;
     top: 0;                     /* 复制按钮位置 */
     right: 0;                   /* 复制按钮位置 */
    }
    
3.2.7 back to top

在页面增加“back to top”按钮。

themes/hugo-theme-docdock/layouts/partials/original/body-aftercontent.html文件的{{ partial "custom-content-footer.html" . }}后面添加一个<p>标签和js代码:

<p id="back-to-top"><i class="fa fa-space-shuttle fa-2x fa-rotate-270" aria-hidden="true"></i></p>
<script>
    var backButton=$('#back-to-top');
    backButton.hide();                    /*首先将#back-to-top隐藏*/
    function backToTop() {
        $('html,body').animate({scrollTop: 0}, 800);
        return false;
    }
    backButton.on('click', backToTop);
    $(window).on('scroll', function () {   /*当滚动条的垂直位置大于浏览器所能看到的页面的那部分的高度时,回到顶部按钮就显示 */
        if ($(window).scrollTop() > $(window).height())
            backButton.fadeIn();
        else
            backButton.fadeOut();
    });
    $(window).trigger('scroll');            /*触发滚动事件,避免刷新的时候显示回到顶部按钮*/
</script>

themes/hugo-theme-docdock/static/theme-original/variant-mine.css文件内添加:

/* 回到顶部按钮 */
p#back-to-top{
    position: fixed;
    bottom: 22%;
    right: 1%;
}
p#back-to-top i{
    color: #1E90FF;
    display: block;
    z-index: -100;
}
p#back-to-top i:hover{
    color: red;
}
3.2.8 页脚

docdock主题页脚(footer):themes/hugo-theme-docdock/layouts/partials/custom-content-footer.html

<footer class=" footline" >
	{{with .Params.LastModifierDisplayName}}
	    <i class='fa fa-user'></i> <a href="mailto:{{ $.Params.LastModifierEmail }}">{{ . }}</a> {{with $.Date}} <i class='fa fa-calendar'></i> {{ .Format "2006/01/02" }}{{end}}
	    </div>
	{{end}}

在页脚(footer)中增加打赏和版权信息。

  1. config.toml文件增加:

    [params]
      copyrighthtml = "Copyright &#xA9; 2020 Komantao. All Rights Reserved."
    [params.reward]                             # 打赏
        enable = true
        wechat = "/images/wechat.png"           # 微信收款二维码
    
    • 在每年的1月1日,修改“2020”为当前年份
  2. 在页脚文件末尾添加:

    {{if and (not .IsHome) (not .Params.chapter) (not .Data.Terms)}}
        {{if .Site.Params.reward.enable}}
         <p style="text-align: center">感谢您的赞赏支持:</p>
         <img class="wechat" src="/images/wechat.png" style="max-width:30%">
        {{end}}
        {{ if .Site.Params.copyrighthtml }}
            <div class="text-center" style="text-align: center">{{.Site.Params.CopyrightHTML | safeHTML}}</div>
        {{ end }}
    {{ end }}
    
3.2.9 打开图片

在新标签页打开图片。

themes/hugo-theme-docdock/layouts/partials/original/body-aftercontent.html<div id="body-inner">标签末尾位置添加:

<script type="text/JavaScript" language="javascript">
    <!--给页面中所有img对象添加onclick事件 -->
    function AddImgClickEvent(){
        var objs = document.getElementsByTagName("img");
        for(var i=0;i<objs.length;i++){
            objs[i].onclick=function(){
                window.open(this.src);
            }
            objs[i].style.cursor = "pointer";
        }
    }
    AddImgClickEvent();
</script>

3.3 CSS

mine.css命名为variant-mine.css,存放在themes/hugo-theme-docdock/static/theme-original目录内:

:root{
    /* body标签:文章正文 */
    --MAIN-TEXT-color:#323232; /* 缺省颜色 */
    --MAIN-TITLES-TEXT-color: #5e5e5e; /* h2-h3-h4-h5标题的颜色 */
    --MAIN-LINK-color:#1C90F3; /* a标签:链接(links)的颜色 */
    --MAIN-LINK-HOVER-color:#DC143C; /* 悬停在链接时的颜色 */
    --MAIN-ANCHOR-color: #1C90F3; /* color of anchors on titles */

    /* nav标签:导航栏(或称侧边栏、菜单栏) */
	/* #sidebar #header-wrapper:导航栏内的header,包括网站图标和搜索栏  */
	--MENU-HEADER-BG-color:#E0FFFF; /* 导航栏内的header的背景颜色 */
    --MENU-HEADER-BORDER-color:#33a1ff; /* 导航栏内的header的边框颜色 */ 
	--MENU-WIDTH:16em; /* 导航栏相对宽度 */
    
	/* #sidebar .searchbox:导航栏内的搜索栏  */
    --MENU-SEARCH-BG-color:#D3D3D3; /* 搜索栏的背景颜色 */
    --MENU-SEARCH-BOX-color: #33a1ff; /* 搜索栏的边框颜色 */
    --MENU-SEARCH-BOX-ICONS-color: #000000; /* 搜索栏中的图标的颜色 */

    /* #sidebar ul.topics > li.parent, #sidebar ul.topics > li.active:活动组件,指被选中的章节或其内容  */
    --MENU-SECTIONS-BG-color:#E0FFFF; /* 导航栏的缺省颜色 */
    --MENU-SECTIONS-LINK-color: #000000; /* 导航栏内链接的颜色 */
    --MENU-SECTIONS-LINK-HOVER-color: #FF0000;  /* 导航栏内链接悬停时的颜色 */
	--MENU-SECTIONS-ACTIVE-BG-color: #87CEFA; /* 活动组件(active section,被选中时)的背景颜色,包含子组件 */
    --MENU-SECTION-ACTIVE-CATEGORY-color: #000000; /* 活动组件的颜色 */
    --MENU-SECTION-ACTIVE-CATEGORY-BG-color: #FDF5E6; /* 活动组件的背景颜色 */
    --MENU-SECTION-HR-color: #20272b; /* 导航栏<hr>分隔符的颜色 */  
}


/* body标签 */
body {
    color: var(--MAIN-TEXT-color) !important;
	font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,Microsoft YaHei,Source Han Sans CN,Source Han Sans SC,Noto Sans CJK SC,WenQuanYi Micro Hei,sans-serif;
	font-weight: 200;
	font-size: 16px !important;
}


/* 导航栏(侧边栏):nav#sidebar */
#sidebar {
    max-width: var(--MENU-WIDTH) !important;
	background-color: var(--MENU-SECTIONS-BG-color);
}
#sidebar #header-wrapper {
    background: var(--MENU-HEADER-BG-color);
	border-bottom: 3px solid var(--MENU-HEADER-BORDER-color);
}
#sidebar #header-wrapper #header a.baselink {
	font-weight: 800;
	font-size: 28px !important;
}
#sidebar #header-wrapper #header span {
	color: #b22222;
}
#sidebar .searchbox {
	border-color: var(--MENU-SEARCH-BOX-color);
    background: var(--MENU-SEARCH-BG-color);
}
#sidebar .searchbox * {
    color: var(--MENU-SEARCH-BOX-ICONS-color);
}
#sidebar ul.topics > a, #sidebar ul.topics section#shortcuts > a {
    font-weight: 800;
	font-size: 20px !important;
}
#sidebar ul.topics > li.parent, #sidebar ul.topics > li.active {
    background: var(--MENU-SECTIONS-ACTIVE-BG-color);
}
#sidebar ul.topics li.active > div > a {
    background: var(--MENU-SECTION-ACTIVE-CATEGORY-BG-color);
    color: var(--MENU-SECTION-ACTIVE-CATEGORY-color) !important;
	font-weight: 800 !important;
}
#sidebar a {
    color: var(--MENU-SECTIONS-LINK-color);
	font-weight: 400;
	padding: 0 !important;
	width: calc(100%) !important;
}

#sidebar a:hover {
    color: var(--MENU-SECTIONS-LINK-HOVER-color) !important;
}
#sidebar hr {
    border-color: var(--MENU-SECTION-HR-color);
}
#sidebar section#shortcuts {
	border-top: 3px solid var(--MENU-HEADER-BORDER-color);
}

#sidebar .parent li, #sidebar .active li{
border-left:1px solid red;
} 

#body .nav{
	width:1.2rem;
}


/* 文章正文:section#sidebar */
@media only screen and (min-width: 780px) {       /* 媒体判断:判断PC浏览器和手机屏幕像素自动调用不同CSS的代码 */
    #body {
	    margin-left: 15em!important;
    }
	#body .padding {
	    padding: 0.5rem 2rem 0.5rem 2rem;
    }
	#top-github-link {
	    position: fixed;
	}
}

#body a.highlight:after {
    display: block;
    content: "";
    height: 1px;
    width: 0%;
    -webkit-transition: width 0.5s ease;
    -moz-transition: width 0.5s ease;
    -ms-transition: width 0.5s ease;
    transition: width 0.5s ease;
    background-color: var(--MAIN-LINK-HOVER-color);
}

/* 固定文章顶栏:#top-bar  */
#top-bar {
	width: 100%;
    position: fixed; top: 0;
	z-index: 99;
	background: #FFEBCD;
	margin: 0rem -1rem 1rem;
}
#breadcrumbs {
	display: inline-block;
}
#top-github-link {
	top: 1.6rem;right: 3rem;
	z-index: 199;
	display: inline-block;
}
/* 文章标签:#tags  */
#tags {
	margin-top: 1.8rem;
}
#tags a.label {
	background-color: #EE82EE;
}
/* 文章顶栏中的TOC:#body div#top-bar div.progress nav#TableOfContents  */
#TableOfContents {
	padding: 2px !important;
}
/* 第二层 */
#TableOfContents > ul > li > ul > li > a {
	padding: 0;
	color: #000000;
}
/* 第三层 */
#TableOfContents > ul > li > ul > li > ul > li > a {
	padding: 0 20px;
}
/* 第四层 */
#TableOfContents > ul > li > ul > li > ul > li > ul > li > a {
	padding: 0 30px;
	color: #FFA500;
}
textarea:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="password"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="text"]:focus, input[type="url"]:focus, input[type="color"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, select[multiple=multiple]:focus {
    border-color: none;
    box-shadow: none;
}
div#body-inner > p.authorline {
    text-align: center;
	background-color: #E0FFFF;
    border-bottom: 1px dashed #183f81!important;
}
h1, h2, h3, h4, h5, h6 {
    color: var(--MAIN-TITLES-TEXT-color) !important;
	font-weight: 700 !important;
}
h1 {
	color: red!important;
}
h2 {
	font-size: 1.8em;
	margin: 20px 0;
	padding-left: 0px;
	line-height: 1.4;
}
h3 {
	font-size: 1.2em;
	line-height: 1.4;
	margin: 10px 0 5px;
	padding-top: 10px;
}
h4 {
	/* text-transform: uppercase;              控制文本的大小写:uppercase 大写 */
	font-size: 1.1em;
	line-height: 1.4;
	margin: 10px 0 5px;
	padding-top: 10px
}
h5, h6 {
	font-size: 1.2em;
}
a {
    color: var(--MAIN-LINK-color);
}
.anchor {
    color: var(--MAIN-ANCHOR-color);
}
a:hover {
    color: var(--MAIN-LINK-HOVER-color) !important;
}

/* 设置表格  */
table th {
	background: #999999;
	text-align: center;
}
table th, table td {
    border: 1px solid #000000;
    padding: 0.2rem;
}
/* 设置引用  */
blockquote {
	background: #E0FFFF;
	border-left: 4px solid #0066FF;
}
blockquote p,blockquote ul {
	margin: 0.5rem !important;
}
/* 设置代码块  */
#body-inner pre {
	background: black;
	padding: 1.5rem 0 0 0!important;
    margin: 0;
	white-space: pre;	
}
code {
	white-space:pre-wrap;
}
#body-inner pre code {
	max-height: 300px;
    overflow: auto;
}
#body img, #body .video-container {
	margin: 1rem auto;
}
/* highlight.js插件的语法高亮  */
.hljs ul {
    list-style: decimal;
    margin: 0 0 0 40px !important;
    padding: 0;
}
.hljs li {
    list-style: decimal-leading-zero;
    border-left: 1px solid #111!important;
    padding: 2px 5px!important;
    margin: 0!important;
    line-height: 14px;
    width: 100%;
    box-sizing: border-box;
}
.hljs li:nth-of-type(even) {
    background-color: rgba(255,255,255,.015);
    color: inherit;
}
.copy-to-clipboard {
	display: none;
}
pre .copy-to-clipboard {
	display: inline-block!important;
	top: 0;
	right: 0;
}
/* 回到顶部按钮 */
p#back-to-top{
    position: fixed;
    bottom: 22%;
    right: 1%;
}
p#back-to-top i{
    color: #1E90FF;
    display: block;
    z-index: -100;
}
p#back-to-top i:hover{
    color: red;
}

Top Bar,文章页面顶部的导航栏,包含4部分:

image-20200122121955654

  • 1:sidebar开关,在小屏幕时显示或隐藏sidebar
  • 2:TOC目录
  • 3:面包屑(breadcrumb)导航,显示路径
  • 4:GitHub源代码仓库内此文章的代码(code)页面,使用GItHub的"Edit-this-page"插件

页面滚动时固定顶栏:

/* 固定文章顶栏:#top-bar  */
#top-bar {
	width: 100%;
    position: fixed; top: 0;
	z-index: 99;
	background: #FFEBCD;
	margin: 0rem -1rem 1rem;
}
#breadcrumbs {
	display: inline-block;
}
#top-github-link {
	top: 1.6rem;right: 3rem;
	z-index: 199;
	display: inline-block;
}

3.4 i18n

复制learn主题中的简体中文语言包zh-cn.toml(或新建)到docDock主题的i18n目录内:

[Search-placeholder]
    other = "搜索..."
[Clear-History]
    other = "清理历史记录"
[Attachments-label]
    other = "附件"
[title-404]
    other = "错误"
[message-404]
    other = "哎哟。 看起来这个页面不存在 ¯\\_(ツ)_/¯。"
[Go-to-homepage]
    other = "转到主页"
[Edit-this-page]
    other = "编辑当前页"
[Shortcuts-Title]
    other = "更多"
[Expand-title]
    other = "展开"

三、页面内容

1、原型

原型是创建新页面内容(运行hugo new命令)时使用的模板,预先配置格式:例如md文件的扉页( front matter)、其它格式等。原型文件应该存放在archetypes目录内。

原型(archetypes/default.md)内的扉页貌似不能进行日期格式转换:

  • date属性只能是date: {{ .Date }},因为之后的日期格式转换基于此date属性。若date: {{.Date.Format "2006-01-02"}},将会触发错误:Error: Failed to process archetype file “default.md”:: template: default:3:19: executing “default” at <.Date.format>: can't evaluate field format in type string

1.1 default.md

default.md:将md文件构建为HTML的页面文件(type:缺省)。

---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
author: "komantao"
LastModifierDisplayName: "komantao"
LastModifierEmail: komantao@hotmail.com
weight: 20
url: {{ replace .Dir "\\" "/" }}{{ replace .Name "-" " " | title }}.html
draft: false
description: "文章描述"
keywords: [keyword1, keyword2, keyword3]
tags: [标签1, 标签2]
categories: [分类1, 分类2]
---
首页描述。

<!--more-->
## 一、概述


## 参考资料

> - []()
> - []()
> - []()

## 操作环境

> - 操作系统:Win 10
> - 

1.2 chapter.md

chapter.md:将md文件构建为HTML的章节文件(type:chapter)。

+++
title = "{{ replace .Name "-" " " | title }}"
date = {{ .Date }}
weight = 5
chapter = true
+++

### 自定义章节序号

# 自定义标题

章节介绍

一定要设置chapter = truetype="chapter"才能生成章节文件。

1.3 slide.md

slide.md:将md文件构建为HTML的幻灯片文件(type:slide)。

+++
title = "Slide title"
type="slide"

theme = "league"
[revealOptions]
transition= 'concave'
controls= true
progress= true
history= true
center= true
+++

# Slide 1

___

## Slide 1.1

- Turn off alarm
- Get out of bed

___

## Slide 1.2

- Eat eggs
- Drink coffee

---

# Slide 2

___

## Slide 2.1

- Eat spaghetti
- Drink wine

___

## Slide 2.2

- Get in bed
- 

docDock主题提供了slide插件,必须配合插件才能生成HTML的幻灯片。

2、页面绑定

内容组织(Content Organization)指存放在content目录内的页面(pages),结构假设为:

├─content           # 网站源代码目录
| ├─_index.md       # 网站主页(整个网站的简介)
| ├─posts           # 一个名为posts的章节文件夹
| | ├─_index.md     # 章节主页(整个章节的简介)
| | └─topic         # 章节内的子页面内容目录(包含md文件和图片)
| | | ├─topic.md    # 章节内的子页面内容(markdown文件)
| | | └─123.jpg     # topic.md文件引用的本地图片

备注:内容文件若引用图片,Hugo建议将其与内容文件放在同一个目录内。

Hugo中的内容组织的管理依赖于Page Bundles(页面绑定)。Page Bundles包括:

  • Leaf Bundle(没有子节点)

  • Branch Bundle(home page、section、taxonomy terms、taxonomy list)

    • home page:网站主页

    • section:章节,在content目录内定义的子目录(多个子页面的集合)

      content目录下的第一级子目录都是一个section。如果想让任意一个子目录成为section,需要在目录下面定义_index.md文件。 所有的section构成一个section tree。

      content
      └── blog                       <-- Section, 因为是content的第一级子目录
          ├── funny-cats
          │   ├── mypost.md
          │   └── kittens            <-- Section, 因为包含_index.md
          │       └── _index.md
          └── tech                   <-- Section, 因为包含 _index.md
              └── _index.md
      

      如果section tree 需要可导航,至少最底层的部分需要定义一个_index.md文件。

3、扉页

扉页( front matter)用来配置文章的标题、时间、链接、分类等元信息,提供给模板调用。可使用的格式有:yaml格式(默认格式,使用3个减号-)、toml格式(使用3个加号+)、json格式(使用大括号{})。除了网站主页外,其它内容文件都需要扉页来识别文件类型和编译文件。

---
title: "xxx"                  # 文章标题
menuTitle: "xxx"              # 文章标题在菜单栏中显示的名称
description: "xxx"            # 文章描述
keywords: ["Hugo","keyword"]  # 关键字描述
date: "2018-08-20"            # 文章创建日期
tags: [ "tag1", "tag2"]       # 自定义标签
categories: ["cat1","cat2"]   # 自定义分类
weight: 20                    # 自定义此页面在章节中的排序优先级(按照数字的正序排序)
disableToc: "false"           # 若值为false(缺省值)时,此页面启用TOC
pre: ""                       # 自定义menu标题的前缀
post: ""                      # 自定义menu标题的后缀
chapter: false                # 若值为true(缺省值)时,将此页面设置为章节(chapter)
hidden: false                 # 若值为true(缺省值)时,此页面在menu中隐藏
LastModifierDisplayName: ""   # 自定义修改者的名称,显示在页脚中
LastModifierEmail: ""         # 自定义修改者的Email,显示在页脚中
draft: false                  # true,表示草稿,Hugo将不渲染草稿
url:                          # 重置permalink,默认使用文件名
type:                         # type与layout参数将改变Hugo寻找该文章模板的顺序
layout: 
---
  • weight属性

    • 缺省时按照date属性的倒序排序(新日期排在前面)
    • 设置时,自定义此页面在章节中的排序(按照数字值的正序排序,数字小的排在前面,若数字值相同,则按照date属性的倒序排序)
  • pre属性

    在菜单栏中的标题前添加前缀:可为数字、文字、图标(Font Awesome库)等。

    +++
    title = "Github repo"
    pre = "<i class='fab fa-github'></i> "
    +++
    
  • menuTitle属性

    • 缺省时调用title属性作为此页面在menu中显示的名称
    • 设置时,自定义此页面在menu中显示的名称

4、主页页面

  1. 新建网站主页文件

    若未创建网站主页,运行hugo server时会提示创建一个网站主页:

    Customize your own home page
    The site is working. Don't forget to customize this homepage with your own. You typically have 3 choices : 
    1. Create an _index.md document in content folder and fill it with Markdown content
    2. Create an index.html file in the static folder and fill the file with HTML content
    3. Configure your server to automatically redirect home page to one your documentation page
    

    使用hugo new XX.md命令在content目录内新建一个_index.md文件作为网站主页:

    $ hugo new _index.md
    E:\Hugo\sites\ebook\content\_index.md created
    
  2. 编写网站主页文件

    • 打开网站主页文件(content/_index.md)文件,编写内容
    • 通常不需要扉页,即使设置了,Hugo会忽略

5、章节页面

章节是包含其他子页面的页面,具有特殊的布局样式:通常仅包含章节序号、章节名称和本章节的简短摘要。

Hugo-theme-learn主题调用chapter.md原型文件来构建章节文件的扉页:

  1. 新建章节页面文件(同时新建了章节目录)

    $ hugo new --kind chapter 个人网站/_index.md
    E:\Hugo\sites\ebook\content\个人网站\_index.md created
    
  2. 编写章节主页文件

    打开章节主页文件(_index.md),编写内容(关键是设置chapter属性值为true):

    +++
    title = "blog"                     # 修改标题
    date = 2019-11-28T13:48:56+08:00   # 修改日期
    weight = 5                         # 修改排序优先级别
    chapter = true                     # true,表示此页面是章节页面
    pre = "<b>X. </b>"                 # 修改在侧边栏显示的前缀
    +++
       
    ### 第二章                         # 修改在章节页面显示的章节序号
    # 个人网站                         # 修改在章节页面显示的标题
    介绍制作个人静态网站的各种工具。      # 修改在章节页面显示的简短摘要
    

    网页的效果示例:

    image-20191128145802960

docDock主题没有提供chapter.md,但可直接使用learn主题的chapter.md

6、内容页面

在项目根目录中执行hugo new XX.md命令,会在content目录中自动创建XX.md文件:

$ hugo new 个人网站/hugo/"hugo.md"
E:\Hugo\sites\ebook\content\个人网站\hugo\hugo.md created
  • 文件名若有空格,则必需使用双引号括起;否则,可省略双引号

    不建议文件名含有空格!!!因为Hugo编译时,会将URL中文件的空格解析为-,从而导致本地图片链接解析失败。

  • 创建内容文件时应该使用子目录来实现文档分类

  • 若不指定--kind type,Hugo默认调用archetypes/default.md原型文件来构建内容页面文件的扉页

7、图片

7.1 引用图片

假设content目录的结构为:

├─content           # 网站源代码目录
| ├─_index.md       # 网站主页(整个网站的简介)
| ├─posts           # 一个名为posts的章节文件夹
| | ├─_index.md     # 章节主页(整个章节的简介)
| | └─topic         # 章节内的子页面内容目录(包含md文件和图片)
| | | ├─topic.md    # 章节内的子页面内容(markdown文件)
| | | └─123.jpg     # topic.md文件引用的本地图片

假设在md文件引用图片时使用Markdown语法为:

![](123.jpg)

Hugo渲染时默认使用“Pretty URLs”,将md文件渲染为目录,在浏览器的地址栏中显示渲染后的路径(baseURL = “http://example.com/",渲染为本地web服务器http://localhost:1313/)。

开发者工具查看本地图片的渲染地址:

<a href="http://localhost:1313/posts/topic/topic/123.jpg" data-featherlight="image"><img src="topic/123.jpg" alt="123"></a>

发现图片的渲染路径比实际多了一个与文件名同名的目录。所以,本地图片渲染失败。

解决方法:基本思路是将“Pretty URLs”转换为“Ugly URLs”。

  • 使用“Pretty URLs”时

    • 方法1:使用“Pretty URLs”的文件命名方法

      md文件分目录存放,文件名统一命名为index.md或_index.md。

      ├─content           # 网站源代码目录
      | ├─_index.md       # 网站主页(整个网站的简介)
      | ├─posts           # 一个名为posts的章节文件夹
      | | ├─_index.md     # 章节主页(整个章节的简介)
      | | └─topic         # 章节内的子页面内容目录(包含md文件和图片)
      | | | ├─_index.md   # 章节内的子页面内容(markdown文件)
      | | | └─123.jpg     # 子页面引用的本地图片
      
      • md文件的扉页的title属性自动将目录名(示例为topic)识别为文件名
      • 本地图片和markdown文件位于同一目录内(目的是将路径简单化)
      • 缺点:一个目录只有一个md文件,文件名统一为index.md或_index.md
    • 不使用“Pretty URLs”的文件命名方法(md文件名使用个性化命名)时

      ├─content           # 网站源代码目录
      | ├─_index.md       # 网站主页(整个网站的简介)
      | ├─posts           # 一个名为posts的章节文件夹
      | | ├─_index.md     # 章节主页(整个章节的简介)
      | | └─topic         # 章节内的子页面内容目录(包含md文件和图片)
      | | | ├─topic.md    # 章节内的子页面内容(markdown文件)
      | | | └─123.jpg     # topic.md文件引用的本地图片
      
      • 方法2:在md文件的扉页内手动设置url参数

        在md文件的扉页中手动设置url为实际路径或“Ugly URLs”

        url: posts/topic         # 手动输入md文件的相对路径
        url: posts/topic.html    # 手动输入md文件的“Ugly URLs”
        

        缺点:每一个有图片的md文件都需要手动设置url参数。

      • 方法3:在原型文件(archetypes/default.md)设置变量自动获取url参数

        自动获取url的实际路径:

        url: {{ .Dir }}     # 自动提取md文件的相对路径
        
        • .Dir:一个页面变量,获取内容文件的路径(相对于content目录)

        在Windows平台下,路径的显示格式为:blog\。本地预览时没问题,发布到网站时,此格式就造成URL乱码(blog%5Chugo.html),导致读取图片失败。

        解决方法:

        • 手动改变格式为:blog/

        • 使用replace替换函数:

          url: {{ replace .Dir "\\" "/" }}
          
          • \是一个转义符,因此使用\\将其转义为普通字符。转义后,将blog\替换为blog/
        • 缺点:一个目录内只能有一个md文件(否则,多个md文件将会渲染为同一个目录,Hugo只能选择一个)

        自动获取“Ugly URLs”:

        url: {{ replace .Dir "\\" "/" }}{{ replace .Name "-" " " | title }}.html
        
        • 此方法最完美:
          • 既不破坏Hugo默认的“Pretty URLs”设置
          • 指定的md文件又能实现“Ugly URLs”:一个目录内可以有多个md文件,md文件与引用图片可以在不同目录内
          • 解决了Windows平台下的路径转换
    • 方法4:在网站配置文件config.toml中设置

      [permalinks]
        blog = "/:sections/:slug/"  # blog是某个指定的目录名
      
      • 此方法不可行(只是演示Permalink(固定链接)的用法),因为不能转换为“Ugly URLs”,渲染路径依然是“Pretty URLs”
    • 方法5:愚蠢的方法,图片复制为2份,分别为Markdown语法和Hugo渲染所用

      • 若不设置relativeURLs(默认),静态资源的路径相对于static目录

        1. md文件和本地图片在同一级目录内,目的是将路径简单化

        2. md文件中引用图片时使用Markdown语法

          ![](123.jpg)
          
        3. 将本地图片复制一份到static目录,按照“Pretty URLs”格式构建路径

          例如md文件在content目录的路径为content/posts/topic.md,则其引用图片在static目录的路径为static/posts/topic/123.jpg

      • 若设置relativeURLs,静态资源的路径相对于content目录

        1. md文件和本地图片在同一级目录内,目的是将路径简单化

        2. md文件中引用图片时使用Markdown语法

          ![](123.jpg)
          
        3. 将本地图片复制一份到content目录,按照“Pretty URLs”格式构建路径

          例如md文件在content目录的路径为content/posts/topic.md,则其引用图片在content目录的路径为content/posts/topic/123.jpg

        4. 在网站配置文件config.toml中设置

          relativeURLs = false   =>   relativeURLs = true
          
  • 使用“Ugly URLs”时

    静态资源的路径相对于content目录,且与Markdown语法使用的路径完全一致。

    1. md文件和本地图片在同一级目录内,目的是将路径简单化

    2. md文件中引用图片时使用Markdown语法

      ![](123.jpg)
      
    3. 在网站配置文件config.toml中设置

      uglyurls = true
      

      使用“Ugly URLs”渲染content目录后,将md文件渲染为html文件:

      content/posts/_index.md   =>   example.com/posts/index.html
      content/posts/topic/topic.md   =>   example.com/posts/topic/topic.html
      
    4. 此方法破坏了url默认设置(“Pretty URLs”),因此需要修改url渲染失败的功能

      • 搜索config.toml

        [[menu.shortcuts]]
          name = "<i class='fas fa-align-justify'></i> 分类"
          url = "/categories.html"  # “Pretty URLs”时,url = "/categories"
          weight = 12
               
        [[menu.shortcuts]]
          name = "<i class='fas fa-bookmark'></i> 标签"
          url = "/tags.html"        # “Pretty URLs”时,url = "/tags"
          weight = 13
        
      • 搜索partials

        hugo-theme-learn主题在页面上显示的标签导航使用“Pretty URLs”:

        image-20191204125335380

        themes/hugo-theme-learn/layouts/partials/header.html文件中禁用此功能:

                <!--                              # 新增,将其转换为注释
             <div id="head-tags">
                {{ partial "tags.html" . }}
                </div>
             -->                              # 新增
        

        或将其转换为.html(“Ugly URLs”)。(不懂修改模板)

7.2 调整图片

  1. 调整图片大小

    添加图像的HTTP参数((width、height),值为CSS值(默认为auto):

    ![Minion](https://octodex.github.com/images/minion.png?height=50px&width=300px)
    
  2. 调整图片显示效果

    添加一个CSS类(classes),值为shadow、border等:

    ![Minion](https://octodex.github.com/images/minion.png?classes=border,shadow)
    

四、模板

Hugo以go语言的html/template库作为模版引擎,将内容通过template渲染成html,模版作为内容和显示视图之间的桥梁。

1、模板类型

Hugo有一套模版查找机制,如果找不到与内容完全匹配的模板,它将搜索上一级。直到找到匹配的模板。如果找不到模板,将使用默认模板。

hugo有三种类型的模版:

  1. Single Template:用于渲染页面内容

  2. List Template:用于渲染一组相关内容

    一个站点下所有内容或一个目录下的内容(section listings、home page、taxonomy lists、taxonomy terms)。homepage(_index.md),是一个特殊类型的list template,homepage是一个站点所有内容的入口。

  3. partial Template:可理解为模版级别的组件,可以被其他模版引用

    通常存放在项目根目录(或主题目录)内的layouts/partial目录。

页面模版查询规则:判断页面是single page还是list page:

  • single page:选择模版themes/主题名/layouts/_default/single.html
  • list page,选择模版themes/主题名/layouts/_default//list.html

Output Format:

  • 根据输出格式的名称和后缀,选择匹配的模版。例如输出格式是rss,后缀是.html,首先看有没有匹配的index.rss.html格式的模版

Language:

  • 根据站点设置的语言选择匹配的模版,比如,站点的语言为fr,模版匹配的优先级是:index.fr.amp.html > index.amp.html > index.fr.html

Layout:

  • 可以在页面头部设置front matter:layout,设置页面用指定的模版进行渲染,例如,页面在目录posts(默认section)下面,layout=custom,查找规则如下:

    layouts/posts/custom.html
    layouts/posts/single.html
    layouts/_default/custom.html
    layouts/_default/single.html
    

type:

  • 如果在页面的头部设置了属性type属性,例如type=event,查找规则如下:

    layouts/event/custom.html
    layouts/event/single.html
    layouts/events/single.html
    layouts/_default/single.html
    

2、模板语法

block:

  • 在父模板中通过{{block “xxx”}}定义一个块

  • 在子模块中通过{{define “xxx”}}定义的内容进行替换

模板引用:

  • 方法一:partial(推荐使用)

    引入定义的局部模板(位置在themes/layouts/partialslayouts/partials目录内):

    {{ partial "chrome/header.html" . }}
    
  • 方法二:template

    在一些比较老的版本中用于引入定义的局部模版,现在在内部模版中仍然使用。

    {{- xxx -}}
    
    • -用于去除空格

      {{- xxx}用于去除{{- 前边的空格、{{ xxx -}用于去除-}}后边的空格、{{- xxx -}}用于去除两边的空格。

Paginator:

  • .Paginator主要用来构建菜单,只能在homePage、listPage中使用

    {{ range .Paginator.Pages }}
       {{ .Title }}
    {{ end }}
    

简码:

  • 简码(shortcodes)相当于一些自定义模版,通过在md文档中引用,生成代码片段,类似于一些js框架中的组件

  • 简码必须在themes/layouts/partials或者layouts/partials目录内定义

  • 简码在模版文件中引用是无效的,只能在content目录下的md文件中引用

  • 引用方式

    {{% msshortcodes params1="xxx" %}}**this** is a text{{% /msshortcodes %}}
    {{< msshortcodes params1="xxx"  >}} **Yeahhh !** is a text {{< /msshortcodes >}}
    
    • %:代表短代码中的内容使用Markdown语法,需要进一步渲染
    • <:代表短代码中间的内容使用HTML语法,不需要渲染

taxonomy:

参阅:Hugo中定制Taxonomy页面

  • Hugo中通过taxonomy来实现对内容的分组。taxonomy需要在站点配置文件中定义:

    [taxonomies]
      category = "categories"
      tag = "tags"
      series = "series"
    
    • 默认情况下,Hugo提供了category、tag两个分类,不需要用户在配置文件中定义,但如果还有其他的taxonomy定义,则默认的tag、category也需要定义
    • 使用方式
      1. 在站点配置文件中添加taxonomy,例如:series
      2. 定义taxonomy list template,例如:layouts/taxonomy/series.html
      3. 在内容文件的front matter中设置term;例如:series = [“s1”,“s2”]
      4. 访问taxonomy列表,例如:localhost:1313/series/s1

变量:

  • Hugo提供了很多不同类型变量用于创建模版,构建站点

Site:

  • 大部分站点变量定义在站点配置文件中(config.[yaml|toml|json] )

    .Site.Menus:站点下的所有菜单
    .Site.Pages:站点下的所有页面
    .Site.Params: 站点下的参数
    

Page:

  • 页面级参数都定义在页面头部的front matter中,例如:

    title = "Hugo"
    date = 2018-08-13T18:29:20+08:00
    description = ""
    draft = false
    weight = 10
    
  • 使用方式

    {{ .Params.xxx }} 或者是 {{ .Site.Params.xxx }}
    

五、简码

简码(shortcode)是内容(content)文件中的简单代码段,调用内置或自定义模板,在Markdown文件中使用HTML语法实现某些功能。

Hugo使用Markdown是因为其内容格式简单。但是,Markdown有其不足,有时被迫在Markdown文件中使用纯HTML来扩展用法。但这与Markdown语法的简单性相矛盾。为了避免这种限制,Hugo创建了简码

简码不能用于模板文件。如果需要在模板中插入简码,则很可能需要 partial template

除了Markdown更简洁外,简码可以随时更新以反映新的类、技术或标准。在网站生成时,Hugo简码将轻松合并到您的更改中。您避免了可能很复杂的搜索和替换操作。

Hugo通过简码扩展了Markdown用法(在不破坏Markdown语法简单性的基础上使用HTML语法)。Typora编辑器不能识别简码,即只能通过Hugo渲染后在HTML网页上才能浏览效果。

1、预设简码

简码在Markdown文件中的调用语法:

# 分隔符使用% %,内含的源代码使用Markdown语法(可被渲染,**World**显示为粗体)
# 从Hugo_V_0.55开始,%作为最外层的分隔符,某些简码不能使用%分隔符,触发错误raw HTML omitted
{{% shortcodename parameters %}}Hello **World**{{% /shortcodename %}}
# 分隔符使用< >,内含的源代码使用HTML语法(不可被渲染,**World**显示为**World**)
{{< shortcodename parameters >}}Hello **World**{{< /shortcodename >}}
  • shortcodename:简码文件(shortcodename.html)的文件名,需要事先定义

  • parameters:参数

  • 简码的优先级高于代码块

    即使将简码放入代码内,Hugo依然识别出简码。若简码文件没有事先定义,Hugo渲染时会触发错误:failed to extract shortcode: template for shortcode “shortcodename” not found。

  • 若想运行简码,简码的源代码不应该放入代码块内

    有的简码可在代码块内运行,有的不行。

  • 若想在代码块内放入简码的源代码,需要破坏简码的语法

    例如:在{{之间添加空格或添加注释符号/* */;使用时,删除增加的部分。

  • 简码的分隔符有% %(Hugo_V_0.55后,作为最外层的分隔符)和< >

    分隔符和大括号之间不能有空格分隔。

  • 简码参数(shortcodename、parameters)之间、分隔符和简码参数之间由空格分隔

    若parameters包含空格,则应该使用引号括起,格式为:name="value"

示例:引用Hugo的预设简码figure,构建HTML5的figure标签显示图片

{ {< figure src="/images/风车.gif" title="网站logo" >}} 

显示效果(经过Hugo渲染后,在HTML网页才能看到效果):

网站logo

简码在网页HTML结构中显示的标签为:

<figure>
    <img src="../images/风车.gif" style="cursor: pointer;"> <figcaption>
            <h4>网站logo</h4>
        </figcaption>
</figure>

Hugo-theme-learn主题的简码:https://learn.netlify.com/en/shortcodes/。

Hugo-theme-docDock主题的简码:https://docdock.netlify.com/shortcodes。

  • alert:突出显示页面中的信息
  • attachments:显示页面附件文件的列表
  • button:在页面中显示可操作的按钮
  • children:列出页面的子页面
  • excerpt:标记页面内容的一部分以供重复使用
  • excerpt-include:显示另一页面中“摘录”(即一部分)内容
  • expand:在页面上显示文本的可打开/折叠部分
  • icon:显示图标
  • mermaid:生成图表(diagram)和流程图(flowchart),其方式与Markdown语法类似
  • notice:构建页面的免责声明
  • panel:突出显示信息或将其放在框中,在文本周围创建一个彩色框
  • revealjs:将内容显示为Reveal.js幻灯片
  • siteparam:获取页面中站点参数变量的值(用于config.toml

2、新建简码

新建简码时,将模板(shortcodename.html文件)放入项目根目录或主题的layouts/shortcodes目录中,Hugo渲染时,会以此目录作为简码的根目录读取简码的HTML文件。

示例:创建一个折叠代码行的简码

  1. 新建简码的HTML文件,假设命名为collapsible.html

    <details>
        <summary style="background-color:#f5f5f5;border:1px solid #ccc;padding:5px;">
            {{ with .Get 0}}{{.}}{{else}}click to expand{{ end }}
        </summary>
        {{.Inner}}
    </details>
    
    • 设置的功能非常简单,只是简单的折叠行,尚未实现折叠代码块的功能
  2. 在Markdown文件中使用简码collapsible

    { {< collapsible "折叠" >}}
    折叠行的代码内容1<br/.>
    折叠行的代码内容2
    { {< /collapsible >}}
    

    显示的效果(经过Hugo渲染后,在HTML网页才能看到效果):

    折叠 折叠行的代码内容1
    折叠行的代码内容2

六、编译

在项目根目录中执行hugo server 命令调用Hugo内置的本地web服务器调试预览博客:

  • --theme:指定主题
  • --watch:修改文件后自动刷新浏览器(v0.15版本后,缺省此参数也可自动刷新)
  • --buildDrafts:编译草稿文件(扉页中的draft值为true)
$ hugo server --theme=hugo-theme-learn --buildDrafts --watch
Building sites … WARN 2019/11/27 16:09:27 .File.UniqueID on zero object. Wrap it in if or with: {{ with .File }}{{ .UniqueID }}{{ end }}

                   | EN
+------------------+----+
  Pages            | 10
  Paginator pages  |  0
  Non-page files   |  1
  Static files     | 77
  Processed images |  0
  Aliases          |  0
  Sitemaps         |  1
  Cleaned          |  0

Total in 75 ms
Watching for changes in E:\Hugo\sites\ebook\{archetypes,content,data,layouts,static,themes}
Watching for config changes in E:\Hugo\sites\ebook\config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop

七、部署

实现:源代码推送到源代码仓库后,自动部署到多个不同的发布网站。

将博客部署到:

  • Pages网站

    Git托管平台免费提供的Pages服务,缺点是必须公开仓库(别人可轻易获取代码)。

    • GitHub Pages

      公开GitHub的发布仓库后,就能免费使用GitHub Pages服务。

      服务器在国外,国内访问速度不稳定。而且,GitHub Pages屏蔽了百度爬虫,百度无法爬取GitHub Pages网页。

    • Gitee Pages

      Gitee(国内的Git免费托管平台)提供的免费Pages服务(发布仓库可私有,但需要手机验证),服务器在国内,国内访问速度快,但受各种政策影响体验。

  • Netlify

    Netlify是GitHub Pages + Travis CI的完美结合。Netlify在线克隆GitHub源代码仓库(可私有),持续集成和自动部署(Travis CI功能)到其提供的免费网站(Pages服务),然后还额外提供了众多的免费服务。

    缺点是服务器在国外,国内访问速度不稳定,而且不能查看发布仓库。

  • 个人网站

    个人在域名提供商(例如freenom)购买域名。在国内CDN提供商注册域名需要公安局备案。

1、代码安全

一次提交推送到多个仓库(目的纯粹是为了源代码安全)。

为了源代码安全,应该将源代码推送到不同的git代码仓库(例如GitHub和Gitee):

  • 方法一:推送到GitHub后,在Gitee导入GitHub仓库

    手动操作:新建仓库 — 导入已有仓库 — 输入已有仓库的git地址。

  • 方法二:手动多次推送到不同仓库

    每一个仓库都手动推送一次:

    git remote add origin <仓库的git地址>     # 关联仓库
    git push -u origin master                # 推送到仓库
    
  • 方法三:将一个提交一次性推送到不同仓库

    将多个地址添加到origin库后,使用git push origin master 一次性push到多个仓库:

    git remote add origin <仓库1的git地址>               # 关联仓库1,别名为origin
    git remote set-url --add origin <仓库2的git地址>     # origin仓库添加仓库2
    git remote set-url --add origin <仓库3的git地址>     # origin仓库添加仓库3,origin仓库共指向3个不同仓库
    git push -u origin master                           # 推送到3个不同的仓库
    

2、手动部署

以GitHub为例。

  1. 在GitHub创建一个源码仓库

    仓库名随意,权限可为私有,用来存储博客的源代码(pbulic目录外的所有文件)。若不需要对源代码进行版本管理,可不需要创建源码仓库。

  2. 在GitHub创建一个发布仓库

    权限公开,才能开启GitHub Pages服务,用来存储博客的发布内容(pbulic目录)。

    • 使用用户仓库

      命名为<username>.github.io,GitHub Pages的网址为http://username.github.io/。一个帐户只能有一个用户仓库,优点是网址相对简单。

    • 使用项目仓库

      命名随意,GitHub Pages的网址为http://username.github.io/xxx。一个帐户可以有多个项目仓库。

  3. Hugo生成发布内容(public目录)

    在项目根目录内运行hugo --theme=<themename>命令(若在config.toml已配置了theme选项,可直接运行hugo命令),将发布内容生成到public目录。

    $ hugo
    Building sites … WARN 2020/01/07 21:30:32 .File.BaseFileName on zero object. Wrap it in if or with: {{ with .File }}{{ .BaseFileName }}{{ end }}
       
                       | EN
    +------------------+-----+
      Pages            |  60
      Paginator pages  |   0
      Non-page files   |  89
      Static files     | 211
      Processed images |   0
      Aliases          |  17
      Sitemaps         |   1
      Cleaned          |   0
       
    Total in 833 ms
    
  4. public目录内创建本地仓库

    $ cd public                         # 进入public目录
    $ git init                          # 新建本地仓库
    $ git add .                         # 暂存当前目录(public目录)
    $ git commit -m "first commit"      # 提交
    
  5. pubilc目录推送到GitHub的发布仓库

    $ git remote add origin git@github.com:username/username.github.io.git
    $ git push -u origin master
    
  6. GitHub根据发布仓库自动创建GitHub Pages网站,浏览器输入网址(https://username.github.io/)就能访问博客

3、Travis CI

手动部署(Manual Deployment)是一个重复性劳动,既繁琐又耗时且容易出错。因为部署流程基本固定,所以应该交由CI/CD工具自动完成。

编写代码只是软件开发的一小部分,更多时间往往花在构建(build)和测试(test)上。为了提高软件开发效率,持续集成、持续交付、持续部署工具层出不穷。在GitHub市场上提供了众多的持续集成服务提供商(工具)。

  • 持续集成(Continuous Integration)

    是一个开发行为,强调开发人员提交新代码后,立刻进行构建、(单元)测试。根据测试结果,即时发现问题,即时修复。

  • 持续交付(Continuous Delivery)

    在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的“类生产环境”(production-like environments)中。例如,完成单元测试后,可以把代码部署到连接数据库的Staging环境中进行更多的测试。若没有问题,可以继续手动部署到生产环境中。

  • 持续部署(Continuous Deployment)

    在持续交付的基础上,将部署到生产环境的过程自动化。

Travis CI是一个使用Ruby语言开发、在线托管、开源的分布式持续集成服务提供商,是CI工具中市场份额最大的一个。Travis CI目前有两个官网:

  • Travis CI:旧平台(已逐步放弃),只对开源项目(公开仓库)免费
  • Travis Pro:新平台,对开源项目和私有项目(私有仓库)免费

通过Travis CI部署Hugo或者Hexo博客在配置时可能麻烦一点,但配置好后非常方便好用,特别是对于Hugo这种没有部署插件的静态网站生成器。

3.1 部署到GitHub Pages

GitHub使用Travis CI非常简单,因为GitHub的token具有推送代码的权限,一切都能在线操作。

3.1.1 两个远程仓库

缺点:公开了发布代码(源代码可私有)。

参阅:使用Travis CI自动部署Hugo博客

  1. GitHub创建2个远程仓库

    • 仓库A:存放Hugo的输入文件(public目录外的所有文件)

      命名随意,权限可为私有。

    • 仓库B:存放博客发布文件(public目录)

      使用用户仓库(<username>.github.io),权限公开,开启GitHub Pages服务。

  2. GitHub生成个人访问令牌(Personal Access Token)

    Travis CI自动部署时,需要push内容到仓库的某个分支,而访问GitHub仓库需要用户授权,授权方式就是用户提供访问令牌给Travis CI。

    在GitHub生成token(路径:帐户Settings — Developer settings — Personal access tokens — Generate new token),至少需要勾选 public_repo权限。建议:勾选repo上的所有项目,别的项目一个都不要选。

  3. 配置Travis CI Pro

    1. 登录(注册)Travis CI Pro

      进入Travis CI官网,登录(注册)Travis CI:

      image-20200114143054489

      首次登录(注册)时,Travis CI请求关联GitHub:

      image-20200114143219508

    2. 激活帐户,选择源代码仓库

      登录后,进入getting_started页面,点击帐户(页面上方的导航栏最右侧登录头像)内的“Settings”:

      image-20200114144017361

      进入个人帐户(MY ACCOUNT)页面:

      image-20200114144415406

      首次使用时,激活“GitHub Apps Integration”。

      “GitHub Apps Integration”支持私有和公开仓库,与GitHub交互时提供增强的安全性。

      点击“Activate”后,弹出页面:

      image-20200114145037486

      • 选择“All repositories”,表示选择GitHub帐户中的所有仓库(包含未来新建)
      • 选择“Only select repositories”,表示只选择GitHub帐户中的指定仓库
      • 点击“Approve & Install”后,可在GitHub(帐户Settings — Applications — Install GitHub Apps)中查看安装结果或重新配置
    3. 同步帐户

      “GitHub Apps Integration”激活完毕后,Travis返回“MY ACCOUNT”页面,并自动(或点击“Sync account”)显示帐户内的仓库。

      image-20200114150416516

    4. 激活源代码仓库

      指定帐户内的某个仓库为源代码仓库。Travis将根据此激活仓库的提交自动部署到博客网站。点击上图中的仓库列表中某一个,即可进入仓库界面:

      image-20200114160144369

      仓库的功能菜单项有:

      • Current:仓库的当前状态

        可查看最新构建的日志(log):

        • hugo命令成功运行时显示:The command “hugo” exited with 0.
        • build命令成功运行时显示:Done. Your build exited with 0.
      • Build History:仓库的构建历史

      • Settings:仓库设置,部署触发条件和添加环境变量(Personal Access Token)

        • 勾选触发条件

          General、Auto Cancellation、Config Import等选项,使用默认值即可。

        • 设置环境变量(Environment Variables)

          在“Environment Variables”中输入token信息后,点击Add按钮: image-20200107164339086

          • NAME:名称任意,例如GITHUB_TOKEN
          • VALUE:在GitHub申请到的Token的值
          • BRANCH:默认值(all branches)
      • 设置好的变量在配置文件中以 ${变量名}来引用

    • Trigger build:手动触发构建

      当源代码仓库已有提交时,可手动触发构建。日常使用时,可忽略此步骤。

      弹出界面使用默认值即可:

      image-20200113151506405

  4. 新建Travis配置文件

    配置文件(.travis.yml)告诉Travis CI如何部署博客,一个完整的构建生命周期为:

    Install apt addons                              # (可选)
    Install cache components                        # (可选)
    before_install                                  # 安装依赖前
    install                                         # 安装依赖
    before_script                                   # 执行脚本前
    script                                          # 执行脚本
    before_cache(for cleaning up cache)             # (可选)
    after_success or after_failure                  # 执行脚本成功(失败)后
    before_deploy                                   # (可选),部署前
    deploy                                          # (可选),部署
    after_deploy                                    # (可选),部署后
    after_script                                    # 执行脚本后
    

    Travis CI的一次构建分为两个阶段:

    • install(安装)阶段:安装所需的依赖项,例如hugo框架
    • script(脚本)阶段:运行构建脚本所需的命令,例如hugo命令、git命令

    在本地项目根目录中新建Travis配置文件(.travis.yml),此文件应该上传到仓库A。如果配置文件不在仓库A中,或者不是有效的YAML,Travis CI将无法构建项目。

    使用Python语言(Travis指定语言示例 ,Python耗时比Golang少)配置Travis环境。

    language: python                      # Travis的语言环境为Python
    python:
      - "3.7"                             # python版本指定为3.7
    branches:                             # 分支白名单限制
      only:
        - master                          # 只有master分支的提交才会触发构建
    env:                                  # 设置环境变量
     global:
       - GH_REF: github.com/username/username.github.io.git    # 发布仓库的Git地址(使用HTTPS协议)
    install:                              # 安装依赖项
      - wget https://github.com/gohugoio/hugo/releases/download/v0.62.2/hugo_0.62.2_Linux-64bit.deb   # 手动下载Hugo指定版本
      - sudo dpkg -i hugo*.deb            # 安装Hugo版本
    script:
      - hugo                                    # 运行hugo命令,生成发布内容(public目录)
    # 将博客发布内容推送到GitHub的发布仓库(不是源代码仓库)
    after_script:
      - cd ./public
      - git init
      - git config user.name "xxx"
      - git config user.email "xxx@xxx.com"
      - git add .
      - git commit -m "Update Blog By TravisCI With Build $TRAVIS_BUILD_NUMBER"
      - git push --force --quiet "https://$GITHUB_TOKEN@${GH_REF}" master:master
       
    # 将博客发布内容推送到GitHub的源代码仓库的另一个分支(需要时,去掉注释,即#号)
    # deploy:
      # provider: pages                           # 部署到GitHub Pages网站
      # skip-cleanup: true                        # 必须为true,否则Travis会删除在构建期间创建的所有文件(即删除了计划上传的文件)
      # local-dir: public                         # 要推送到GitHub Pages的目录,可以指定为当前目录的绝对路径或相对路径
      # on:
        # branch: master                          # 博客源代码所在分支  
      # target-branch: xxx                        # 将local-dir强制推送到哪个分支(自定义,名称不能与源代码分支名相同),默认为gh-pages
      # github-token: $GITHUB_TOKEN               # $GITHUB_TOKEN是一个变量:名称是Travis定义名称,值是GitHub的personal access token的值
      # fqdn: xxx                                 # 使用自定义域名
      # verbose: true                             # true,表示显示Travis的log
      # keep-history: true                        # true,表示保持target-branch分支的提交记录
    
    • Travis是在线构建,不占用本地空间
    • 若仓库B的Git地址(GH_REF变量)填写错误时,不提示错误且不会上传到仓库B
    • 部署(deploy)环节的内容:是将发布内容上传到源代码仓库(仓库A)的另一个分支(target-branch名称不能与源代码分支名相同,否则覆盖源代码)
  5. 本地仓库

    在本地项目根目录中新建一个本地仓库存放Hugo的输入文件(public目录外的所有文件)。

    1. 创建忽略文件

      本地仓库不应该包含public目录(Travis会在线生成,可忽略)、themes目录(若自定义了主题,则不能忽略)和resources目录(可忽略)。

      忽略文件(.gitignore)内容假设为:

      public/
      resources/
      
    2. 当前项目(假设为book)创建本地仓库并推送到GitHub的仓库A

      $ git init
      Initialized empty Git repository in E:/Hugo/sites/book/.git/
      $ git add .
      $ git commit -m "generator code"
      $ git remote add github git@github.com:username/xxx.git
      $ git push -u github master
      
  6. 持续集成和自动部署

    若Travis设置了“提交触发build”,本地仓库的每次推送到仓库A,Travis CI都会得到提示,然后根据仓库A中的配置文件(.travis.yml)自动进行部署,实现了博客的自动持续集成(仓库B根据仓库A的内容自动更新,GitHub Pages网站根据仓库B的内容自动更新)。

    简单一句话:只需要将写好的文章推送到GitHub的仓库A,不需要编译、不需要推送到仓库B、 甚至连Hugo都可以不安装(假设使用主题没有自定义)。

3.1.2 一个远程仓库

缺点:源代码和发布代码都公开了。

方法一:参阅利用Travis CI和Hugo將Blog自動部署到Github Pages

  1. 在GitHub创建一个远程仓库

    存放Hugo的输入文件和博客发布文件。命名为<username>.github.io,权限为公开,开启GitHub Pages服务。

  2. GitHub生成个人访问令牌(TOKEN)和配置Travis CI

    步骤同上。

  3. 新建Travis CI的配置文件(.travis.yml

    # https://docs.travis-ci.com/user/deployment/pages/
    # https://docs.travis-ci.com/user/reference/xenial/
    # https://docs.travis-ci.com/user/languages/python/
    # https://docs.travis-ci.com/user/customizing-the-build/
       
    dist: xenial
    language: python
    python:
      - "3.7"
       
    # install - install any dependencies required
    install:
        # install latest release version
        # Github may forbid request from travis container, so use tor proxy
        # - curl -fsL --socks5-hostname 127.0.0.1:9050 https://api.github.com/repos/gohugoio/hugo/releases/latest | sed -r -n '/browser_download_url/{/Linux-64bit.deb/{s@[^:]*:[[:space:]]*"([^"]*)".*@\1@g;p;q}}' | xargs wget
        - wget https://github.com/gohugoio/hugo/releases/download/v0.62.2/hugo_0.62.2_Linux-64bit.deb
        - sudo dpkg -i hugo*.deb
       
    # script - run the build script
    script:
        - hugo
       
    deploy:
      provider: pages
      skip-cleanup: true
      github-token: $token
      email:
      name:
      verbose: true
      keep-history: true
      local-dir: public
      target_branch: master  # branch contains blog content
      on:
        branch: code  # branch contains Hugo generator code
    
  4. 本地仓库

    1. 创建忽略文件

      本地仓库不应该包含public目录(Hugo本地编译时产生)、themes目录(存放主题)和resources目录(由主题产生)。

      忽略文件(.gitignore)内容假设为:

      public/
      resources/
      
    2. 当前项目(假设为book)创建本地仓库

      $ git init
      Initialized empty Git repository in E:/Hugo/sites/book/.git/
      
    3. 分支A(code分支)

      命名为code,存放Hugo的输入文件(博客工程文件,即public目录外的文件)。

      $ git checkout -b code
      # 在code分支
      $ git add .
      $ git commit -m "generator code"
      $ git remote add github git@github.com:username/username.github.io.git
      $ git push -u github code
      
    4. 分支B(master分支)

      命名为master,博客发布文件(即public目录)存放在master分支。

      # 在code分支
      $ hugo                                   # 生成public目录
      $ HUGO_TEMP_DIR=$(mktemp -d)             # 设置临时变量
      $ cp -R public/* "$HUGO_TEMP_DIR"        # public目录复制到临时变量
      $ git checkout --orphan master           # 创建master分支
      # 在master分支
      $ rm .git/index                          # 清除暂存区
      $ git clean -fdx                         # 清除未跟踪文件,当前目录只剩下.git目录
      $ cp -R "$HUGO_TEMP_DIR"/. .             # public目录内容复制到当前目录
      $ git add .
      $ git commit -m 'initial blog content'
      $ git push -u github master
      $ [[ -d "$HUGO_TEMP_DIR" ]] && rm -rf "$HUGO_TEMP_DIR"   # 删除临时变量
      # 切换到code分支,以后应该只在code分支
      $ git checkout code
      
  5. 持续集成

    如果上述步骤中设置了“提交触发build”,本地仓库的每次推送到code分支,Travis CI会自动进行部署,将上传的博客内容(code分支)更新到仓库的master分支中,实现了博客的持续集成(GitHub Pages网站根据master分支内容自动更新),不再需要关心travis状态。

方法二:

  • 方法一略显复杂,简捷方法是在两个GitHub仓库方法中的Travis配置文件(.travis.yml)中启用“deploy”配置,就能实现在一个仓库内实现源代码分支和发布分支。

3.2 部署到Gitee Pages

GitHub Pages和Netlify的服务器在国外,国内访问速度慢且稳定性差。解决这一问题的最优解是购买个人域名使用CDN。对于不想购买个人域名的人来说,使用Gitee Pages(码云的Pages服务)是当前的最优解决方案。

码云仓库(可为私有,GitHub的必需公开)开启Pages服务需要绑定手机号(不需要在公安局备案)。码云是国内的Git托管平台,所以在国内访问速度超快(对比GitHub)且稳定。仓库有更新时,Gitee Pages貌似不会自动更新,需要手动更新(GitHub Pages是自动更新)。

Gitee使用Travis CI比较麻烦,因为Gitee的token没有推送代码的权限,所以需要下载Travis CI到本地,然后在本地加密私钥文件或密码,最后使用加密后的密码push。

参阅:travis官网文档手摸手教你搭建Travis CI持续集成和自动化部署使用travis-ci自动部署hexo博客Hexo集成Travis CI

使用Travis CI部署到Gitee的过程:

  1. 源代码仓库是GitHub仓库

    因为Travis CI只能使用GitHub帐户注册,因此只能绑定GitHub仓库为源代码仓库。

  2. 发布仓库是Gitee仓库

    修改Travis配置文件(.travis.yml)中的GH_REF变量(发布仓库,为了区分不同的仓库,名称可自定义命名)的值为Gitee仓库的git地址:

    env:                                  # 设置环境变量
     global:
       - GH_REF: github.com/username/username.github.io.git    # GitHub发布仓库的Git地址(使用HTTPS协议)
       #- GE_REF: gitee.com/username/username.git               # Gitee发布仓库的Git地址(使用HTTPS协议)
    
  3. 自动验证密码

    Gitee的token没有推送代码的权限,所以此命令失效:

    git push --force --quiet "https://$Gitee_TOKEN@${GE_REF}" master:master    # 强制推送到Gitee发布仓库
    

    改用其它方式验证密码:

    1. 使用https协议,显式添加用户名、密码进行推送

      git push --force --quiet "https://用户名:密码@${GE_REF}" master:master
      

      缺点:彻底暴露Gitee帐户的用户名和密码(即使GitHub仓库是私有),风险极大。

    2. 使用ssh协议,使用私钥文件免密推送

      git push --force --quiet "git@gitee.com:用户名/用户名.git" master:master
      

      缺点:需要将本地的私钥文件推送到GitHub仓库,风险极大。

    3. 通过加密私钥文件或密码

  4. 具体的Travis配置文件(.travis.yml)与GitHub的基本一致

3.3 Travis加密

无论是使用token(GitHub,token在Travis CI中显式显示),还是显式使用密码或上传或ssh私钥(Gitee),即使源代码仓库是私有的,也是存在极大风险。

Travis提供了加密服务,可以对密码或者私钥文件进行加密。

  1. 本地安装Ruby

    Travis客户端使用Ruby语言编写,所以先安装Ruby。(参阅:Ruby 安装 - Windows

    不建议安装Ruby for Windows。因为Windows不支持openssl验证,导致Travis对密钥文件解密时触发错误:The command “openssl xxx -in id_rsa.enc -out ~/.ssh/id_rsa -d” failed and exited with 1 during。

    验证Ruby和gem(安装Ruby时自动安装)是否安装成功:

    $ ruby -v
    ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x64-mingw32]
    $ gem -v
    3.1.2
    
  2. 本地安装Travis客户端

    $ gem install travis
    

    验证是否安装成功:

    $ travis -v
    D:/Program/Ruby/lib/ruby/gems/2.7.0/gems/highline-1.7.10/lib/highline.rb:624: warning: Using the last argument as keyword parameters is deprecated
    1.8.10
    
  3. 关联GitHub源代码仓库

    (克隆GitHub源代码仓库)进入本地工程目录,输入命令:

    $ travis login --auto --pro
    Username:xxx                          # 输入GitHub帐户名
    Password for xxx: xxx                 # 输入GitHub帐户密码
    Successfully logged in as xxx!
    
    • –pro:绑定travis-ci.com,缺省时绑定travis-ci.org
  4. 加密

    1. 加密本地系统的私钥文件(~/.ssh/id_rsa文件),使用SSH协议push

      $ travis encrypt-file ~/.ssh/id_rsa --add
      # 下面是命令行打印的日志
      Detected repository as xxx/xxx, is this correct? |yes| yes
      encrypting ~/.ssh/id_rsa for xxx/xxx
      storing result as id_rsa.enc
      storing secure env variables for decryption
      Make sure to add id_rsa.enc to the git repository.
      Make sure not to add ~/.ssh/id_rsa to the git repository.
      Commit all changes to your .travis.yml.
      
      • 第一行:运行命令,加密~/.ssh/id_rsa文件

      • 第二行:检测Travis绑定的GitHub源代码仓库,若正确,输入yes

      • 第三行:为GitHub源代码仓库加密本地系统的私钥文件

      • 第四行:在本地工程根目录内生成id_rsa.enc文件(已加密),.travis.yml内自动添加(清空所有备注)以下配置信息

        before_install:
        - openssl aes-256-cbc -K $encrypted_xxx_key -iv $encrypted_xxx_iv
          -in id_rsa.enc -out ~\/.ssh/id_rsa -d
        

        实际使用时,要将\去掉,否则会编译失败。

      • 第五行:存储用于解密的安全环境变量

      • 第六行:将id_rsa.enc文件推送到GitHub源代码仓库

      • 第七行:不能将本地系统的私钥文件推送到GitHub源代码仓库

      • 第八行:.travis.yml的修改推送到GitHub源代码仓库

      .travis.yml的最终结果:

      language: python                                     # Travis的语言环境为Python
      python:
        - "3.7"                                            # python版本指定为3.7
      branches:                                            # 设置分支白名单
        only:
          - master                                         # 只有master分支的提交才会触发构建
      addons:
        ssh_known_hosts:
        - gitee.com
      env:                                                 # 设置环境变量
       global:
         # - GH_REF: github.com/koman/koman.github.io.git    # GitHub发布仓库的Git地址(使用HTTPS协议)
         - GE_REF: gitee.com:koman/koman.git               # Gitee发布仓库的Git地址(使用SSH协议)
      before_install:                                      # 加密密码
        - openssl aes-256-cbc -K $encrypted_abf3a382006f_key -iv $encrypted_abf3a382006f_iv -in id_rsa.enc -out ~/.ssh/id_rsa -d
        - eval "$(ssh-agent -s)"                           # 开启ssh-agent,即允许使用ssh命令
        - chmod 600 ~/.ssh/id_rsa                          # 给予id_rsa文件权限,避免警告
        - ssh-add ~/.ssh/id_rsa                            # 将私钥添加到ssh
      install:                                             # 安装依赖项
        - wget https://github.com/gohugoio/hugo/releases/download/v0.62.2/hugo_0.62.2_Linux-64bit.deb   # 手动下载Hugo指定版本
        - sudo dpkg -i hugo*.deb                           # 安装Hugo版本
      script:
        - hugo                                             # 运行hugo命令,生成发布内容(public目录)
      after_script:                                        # 博客发布内容推送到发布仓库
        - cd ./public
        - git init
        - git config user.name "koman"
        - git config user.email "komantao@hotmail.com"
        - git add .
        - git commit -m "Update Blog By TravisCI With Build $DATE"
        # - git push --force --quiet "https://${Github_TOKEN}@${GH_REF}" master:master
        - git push --force --quiet "git@${GE_REF}" master:master
            
      # 博客发布内容推送到GitHub的源代码仓库的另一个分支(需要时,去掉注释,即#号)
      # deploy:
        # provider: pages                           # 部署到GitHub Pages网站
        # skip-cleanup: true                        # 必须为true,否则Travis会删除在构建期间创建的所有文件(即删除了计划上传的文件)
        # local-dir: public                         # 要推送到GitHub Pages的目录,可以指定为当前目录的绝对路径或相对路径
        # on:
          # branch: master                          # 博客源代码所在分支  
        # target-branch: xxx                        # 将local-dir强制推送到哪个分支(自定义,名称不能与源代码分支名相同),默认为gh-pages
        # github-token: $GITHUB_TOKEN               # $GITHUB_TOKEN是一个变量:名称是Travis定义名称,值是GitHub的personal access token的值
        # fqdn: xxx                                 # 使用自定义域名(即不使用GitHub Pages网站)
        # verbose: true                             # true,表示显示Travis的log
        # keep-history: true                        # true,表示保持target-branch分支的提交记录
      
      • 使用了Ruby for Windows,因为Windows不支持openssl验证,导致Travis对密钥文件解密时触发错误,建议改用Ruby for Linux
    2. 加密密码,使用HTTPS协议push

      如果密码中有特殊符号,需要转义:例如@需要转成%40。

      $ travis encrypt gitee_password=xxx --add
      

      .travis.yml文件的global变量增加一个secure字段

      env:
        global:
        - secure: xxxxxx   # 加密后的密码
      

      .travis.yml的最终结果:

      language: python
      python:
      - '3.7'
      branches:
        only:
        - master
      env:
        global:
        - GE_REF: gitee.com/xxx/xxx.git
        - secure: xxxxxx
      install:
      - wget https://github.com/gohugoio/hugo/releases/download/v0.62.2/hugo_0.62.2_Linux-64bit.deb
      - sudo dpkg -i hugo*.deb
      script:
      - hugo
      after_script:
      - cd ./public
      - git init
      - git config user.name "xxx"
      - git config user.email "xxx@xxx.com"
      - git add .
      - git commit -m "Update Blog By TravisCI With Build $DATE"
      - git push --force --quiet "https://xxx:${gitee_password}@${GE_REF}" master:master
      
      • Ruby for Windows使用加密密码方式成功

4、Netlify

Netlify是国外一家提供静态网络托管服务的综合平台,专注于静态网站托管的web服务,完美且免费支持ssl、域名绑定、http/2和TLS,官网宣传其是一个工作流(workflow),包含构建一个网站所需的一切。

Netlify的功能:

  • 托管静态资源

    与GitHub Pages一样,但比GitHub Pages强多了,而且速度也快。两者的对比在netlify官网有介绍。

  • 将静态网站部署到CDN,加速国内访问

    GitHub Pages无法进行CDN加速。Netlify能够使用自带CDN加速服务。

  • 持续部署(Continuous Deployment)

    当博客源码提交推送到托管平台后,Netlify就会自动运行build command(自动克隆博客源码仓库,自动判断博客框架,自动运行build命令生成博客发布代码),自动部署到绑定域名。

    Netlify更关注于前端(网站或APP)的持续集成与持续部署,这是它与其他持续集成工具最大的区别。而且,Netlify的使用非常简单。

  • 可以添加自定义域名(个人购买的域名)

  • 可以启用免费的TLS证书,启用HTTPS

  • 自带CI、支持重定向、插入代码、打包和压缩JS和CSS、压缩图片、处理图片

    重定向(也称URL转发)。GitHub Pages对redirects、重定向(也称URL转发)支持并不友好,如果放弃GitHub Pages提供的username.github.io子域名时很麻烦。对比之下,从username.netlify.com重定向(重定向编码是301或302)至新发布网站时很简单。重定向后,搜索引擎可以清楚识别你的页面已被转移,从而对你的新页面进行重新排名。所以即使哪天Netlify车毁人亡,只要设定好了重定向,你的网站也不会受到任何影响。

4.1 部署流程

  1. 登录(注册)Netlify,关联GitHub帐户

    Netlify提供邮箱注册和包括GitHub在内的第三方验证登陆(注册)。

    image-20200113165140198

    1. 选择第三方(GitHub)验证登陆(注册)

    2. GitHub授权验证登陆Netlify

      image-20200113165555863

    3. 关联GitHub账户

      登录后,页面自动(或点击导航栏上方的Netlify图标)跳转到“New site from Git”,点击“New site from Git”:

      image-20200113170641881

      页面跳转到“Create a new site”,点击GitHub:

      image-20200113170934010

      Netlify首次关联GitHub仓库时,弹出窗口:

      image-20200113171237033

      点击“Authorize Netlify by Netlify”同意授权后,Netlify就有权访问GitHub仓库内容。

  2. 选择仓库

    授权完毕后,页面跳转到“Install Netlify”,选择GitHub的源代码仓库(可为私有仓库):

    image-20200113171953598

    • 备注:可在GitHub中修改Netlify的配置:帐户Settings — Applications

    点击Install后,等待一段时间,页面跳转回“Create a new site”,显示关联后的仓库:

    image-20200113173158915

  3. 构建选项和部署

    点击上图中的已关联仓库,进入第3步骤:

    image-20200113173949722

    • Owner:Netlify自动识别,已绑定了托管平台的仓库

    • Branch to deploy:被部署分支,存储源代码的分支

    • Build command:构建命令

      Netlify会自动判断博客框架,自动显示命令,可自定义参数。

    • Publish directory:发布目录,默认为public

    • show advanced:提示将netlify.toml添加到存储库中,可获得更大的灵活性

      在本地项目根目录内新建netlify.toml文件,保存后上传到GitHub的源代码仓库:

      [build]
      publish = "public"
      command = "hugo"
      [context.production.environment]
      HUGO_VERSION = "0.62.2"
      HUGO_ENV = "production"
      HUGO_ENABLEGITINFO = "true"
      [context.split1]
      command = "hugo --enableGitInfo"
      [context.split1.environment]
      HUGO_VERSION = "0.62.2"
      HUGO_ENV = "production"
      [context.deploy-preview]
      command = "hugo -b $DEPLOY_PRIME_URL"
      [context.deploy-preview.environment]
      HUGO_VERSION = "0.62.2"
      [context.branch-deploy]
      command = "hugo -b $DEPLOY_PRIME_URL"
      [context.branch-deploy.environment]
      HUGO_VERSION = "0.62.2"
      [context.next.environment]
      HUGO_ENABLEGITINFO = "true"
      

      Netlify的构建环境使用Unix。

  4. 构建并发布网站

    前面3个步骤完成后,点击上图中的“Deploy site”按钮(Netlify自动构建并发布博客内容)后,开始构建并发布网站:

    image-20200113175351176

    • 点击上方导航栏中的“Deploys”,可实时显示部署过程(log日志):
      • 仅用30秒,Netlify就完成了整个CD过程:Finished processing build request in 30.640996398s

    网站发布成功后,网站地址:“https://” + 随机生成的子域名 + “.netlify.com”。

    image-20200113180150738

  5. 美化Netlify子域

    默认情况下,基于Netlify子域访问站点。若不喜欢Netlify随机子域且没有购买自定义域名,可以将Netlify子域美化(自定义名称):

    • 方法一:点击上图中的“Domain Settings”
    • 方法二:点击上方导航栏中的“Settings” — “General” — “Site details” — “Change site name”
    • 方法三:“Settings” — “Domain management” — “Domains” — “Custom domains”

    image-20200113184702770

  6. 自定义域名

    点击上上图中的“Set up a custom domain”,进入添加域名页面:

    image-20200117124617111

    输入已购买的域名(不包含www),点击“Verify”后设置DNS(以GitHub Pages示例):

    image-20200117125825508

    • Check DNS configuration:因为尚未将域名解析到Netlify服务器。此时,需要到域名提供商绑定(购买)的域名下添加一条CNAME解析,解析的主机记录对应Netlify的子域名值(xxx.netlify.com

    要求必须拥有域名所有权才能完成所有步骤,所以自定义域名不能使用Pages网站。

    image-20200117130503668

    由于没有个人域名,此步骤暂停。

  7. 网站(Netlify子域)设置

    路径:选择网站(Netlify子域)— 导航栏点击“Settings” — Build & deploy — 编辑设置。

    设置内容有:持续部署命令、webhooks、构建映像、环境变量、注入代码、资产优化(压缩CSS/JS/图片文件)、预渲染等。

4.2 Netlify CMS

内容管理系统(content management system,简称CMS)是指在一个合作模式下,用于管理工作流程的一套制度。该系统可应用于手工操作中,也可以应用到电脑或网络。作为一种中央储存器(central repository),内容管理系统可将相关内容集中储存并具有群组管理、版本控制等功能。版本控制是内容管理系统的一个主要优势。

Netlify CMS是一款团队的编辑工具,但可以作为个人博客的在线编辑工具,可以随时随地修改、编辑博客内容,而不用考虑多设备同步的问题。

4.3 页面重定向

Netlify提供了自定义页面重定向的功能。如果你的域名或者文章结构发生了变化,可以借助重定向功能,将原来的文章URL重定向到现在的地址。

新建一个netlify.toml文件,存放在博客的根目录下,在里面添加以下内容:

[[redirects]]
  from = "https://原始域名/*"
  to = "https://自定义域名/:splat"
  force = true

4.4 Netlify的坑

  1. Netlify通过API ID绑定GitHub仓库

    若删除了GitHub的源代码仓库,即使之后重建(名称一样),Netlify也自动部署失败。因为绑定仓库的API ID已改变,需要重新绑定。

  2. 对TOC的分层理解

    config.toml内设置startLevel = 2的前提下:

    • Netlify的TOC从H1标题(第一层)开始构建<ul>标签
    • Hugo可以从H2标题(第二层)开始构建<ul>标签

    解决方法:在config.toml内设置startLevel = 1

  3. 对嵌套列表的理解

    无序列表和有序列表混用时,Hugo正常显示,Netlify有时会格式错乱。添加了netlify.toml文件后,格式错乱问题自动解决。

5、webhook

在web上,越来越多的操作被描述为事件。webhook(网络钩子)是一种web回调或者http的push API,是向APP或者其他应用提供实时信息的一种方式。Webhook在数据产生时立即发送数据,也就是你能实时收到数据。这一种不同于典型的API,需要用了实时性需要足够快的轮询。这无论是对生产还是对消费者都是高效的,唯一的缺点是初始建立困难。webhook是一种实现事件响应的方式,用于在项目发生事件时通知外部服务器。

以Gitee示例演示webhook的用法。

原理:

webhook流程图

  1. 本地执行git push命令,push代码到Gitee仓库
  2. Gitee接收到push请求后,调用自己服务器上的一个接口
  3. 接口是一个脚本文件,可使用php、Python等脚本语言编写,实现命令的自动执行:例如git pull和重启服务等命令

八、CDN

GitHub Pages和Netlify的服务器都部署在海外,在国内访问本博客的速度会比较慢 (Ping 下来100到200多毫秒)。解决这一问题的最优解就是使用CDN。

CDN(Content delivery network或Content distribution network,内容分发网络)是指一种通过互联网互相连接的计算机网络系统,利用最靠近每位用户的服务器,更快、更可靠地将音乐、图片、影片、应用程序及其他文件发送给用户,来提供高性能、可扩展性及低成本的网络内容传递给用户。

简单来说,CDN就是部署在世界各地的缓存服务器,它们会提前缓存网站上的资源,然后当用户想要访问相关资源时,直接从CDN服务器上取就可以了。这样不仅可以增加访问速度、减少访问延迟,还可以减缓网站服务器上的压力。

要使用CDN,需要在域名提供商更改DNS服务器,变为它提供的域名服务器地址。CDN服务提供商有很多,国内的有七牛云、阿里云、腾讯云等,国内的CDN都要求域名在公安局备案。国外的不需要备案。

Cloudflare是全球最大的DNS服务提供商之一 (号称全球最快的DNS1.1.1.1就是它搞的)。除此之外,还提供CDN、SSL证书、DDos保护等服务,Cloudflare还与百度有合作,在国内部署有大量的节点,还能顺便解决百度爬虫无法抓取GitHub Pages的问题。

Cloudflare注册一个帐号,注册好后点击“Add site”添加你的网站:

image-20200114010426839

没有个人网站,此步骤暂停。

九、评论系统

若使用国内的评论插件,前提是网站需要在公安局备案,因此转向使用国外的评论插件。

1、Gitalk

官方的GitHub仓库: https://github.com/gitalk/gitalk/

官方网站:https://gitalk.github.io/

Gitalk是一个基于GitHub Issue和Preact开发的评论插件,支持多种语言 (包括en、zh-CN、zh-TW、es-ES、fr)并自动判断当前语言。最重要的是Gitalk使用的是GitHub Issue的api,不依赖任何第三方平台。也就是说,只要Github不倒闭,你的评论系统就不会被关闭。

原理:Hugo是将Markdown文档按照主题 (包括HTML模板、CSS、JavaScript等) 编译成静态网页。所以只需要将Gitalk作为一个<div>插入到HTML模板中,然后在config.toml中添加相关配置,就可以实现“评论区”了。

步骤为:

  1. 新建一个评论仓库

    新建一个仓库(或博客发布仓库)作为存放评论的仓库(The repo to store comments),点击菜单栏中的“Settings”,勾选“Issues”,Gitalk会将评论放在这个仓库的issues里面。

  2. 注册Github Application

    在GitHub注册一个Oauth Application,让Gitalk有权限操作GitHub上的仓库。

    • 方法一:帐户“Settings” — “Developer settings” — “OAuth Apps”— “New OAuth App”
    • 方法二:点击链接Github Oauth Application

    OAuth application的新建页面:

    image-20200120112205356

    • 点击“Register application”注册后,记下Client IDClient Secret备用
    • 注册后,可在“Developer settings” — “OAuth Apps"中查看或修改
  3. 添加模板gitalk.html

    在主题的layouts/partials目录中新建gitalk.html文件:

    {{ if .Site.Params.enableGitalk }}
        <div id="gitalk-container"></div>
        <link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css">
        <script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
        <script>
            const gitalk = new Gitalk({
                clientID: '{{ .Site.Params.Gitalk.clientID }}',
                clientSecret: '{{ .Site.Params.Gitalk.clientSecret }}',
                repo: '{{ .Site.Params.Gitalk.repo }}',
                owner: '{{ .Site.Params.Gitalk.owner }}',
                admin: ['{{ .Site.Params.Gitalk.owner }}'],
                id: location.pathname, // Ensure uniqueness and length less than 50
                distractionFreeMode: false // Facebook-like distraction free mode
            });
            (function() {
                if (["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1) {
                    document.getElementById('gitalk-container').innerHTML = 'Gitalk comments not available by default when the website is previewed locally.';
                    return;
                }
                gitalk.render('gitalk-container');
            })();
        </script>
    {{ end }}
    
    • 建议直接将gitalk的资源文件下载放到项目目录中(本地引用gitalk.min.js插件)
    • 使用国内的CDN(https://www.bootcdn.cn/搜索gitalk)在线引用

    主题(themes/hugo-theme-docdock/layouts/partials/custom-content-footer.html)调用gitalk.html文件,放置位置在版权信息前面:

    {{ partial "gitalk.html" . }}
    
  4. 配置config.toml

    config.toml中添加以下配置(参数名称对应gitalk.html文件中的命名):

    [params]
        enableGitalk = true            # true时,开启Gitalk评论插件
    [params.gitalk] 
        clientID = "xxx"               # GitHub应用程序(Gitalk)生成的client ID
        clientSecret = "xxx"           # GitHub应用程序(Gitalk)生成的client secret
        repo = "discuss"               # 存储评论(comments)的仓库名
        owner = "xxx"                  # GitHub仓库的所有者(owner)的GitHub ID
        admin= "xxx"                   # GitHub仓库的所有者和合作者(collaborators),指对仓库具有写访问权限的用户
        id= "location.pathname"        # 页面的唯一ID,标记评论是哪个页面,并且长度小于50
        labels= "gitalk"               # Github的issue标签
        perPage= 15                    # 分页大小(Pagination size),最大为100
        pagerDirection= "last"         # 评论排序方向,可用值为'last'和'first'
        createIssueManually= false     # true时, 无干扰模式(在评论仓库的issue内不显示管理员登录信息)
        distractionFreeMode= false     # true时,启用热键(cmd|ctrl + enter)提交评论
    
    • 配置请参考:https://github.com/gitalk/gitalk
  5. 初始化评论区

    设置好后,将博客内容推送到发布网站,首次启用Gitalk需要初始化(运行hugo server时,gitalk.html文件设置不启用Gitalk)。未初始化时,弹出警告提示如下:

    image-20200113225215637

    使用注册Github Application时的账号登陆并授权:

    image-20200113225528620

    授权后进入每一篇博客刷新页面,才能激活每一篇博客的评论区。

十、SEO

SEO(Search Engine Optimization,搜索引擎优化),利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。目的是让其在行业内占据领先地位,获得品牌收益。

检查网站是否被收录:Google/百度搜索框输入site:yoursite.github.io

验证网站:Google搜索引擎百度搜索引擎

验证方法:

  • 文件验证(简单方便,推荐使用)
  • html标签验证
  • CNAME验证

网站上线后,做SEO时需要检查如下内容:

  • 集成Google Analytics或百度统计

  • 为页面增加header信息,如title和description

  • sitemap.xml

    将网站生成工具自动生成的站点地图的URL提交给Google Webmaster Tools。

  • robots.txt

    阻止搜索引擎爬取网站上的敏感网页。

  • 结构化数据:可以帮助爬虫理解页面内容,参考HTML5的结构化数据

1、网站内容优化

  1. 描述(description)

    config.toml中设置网站描述信息:

    [params]
        description = "xxxxxx"
    

    在每篇文章的扉页中设置文章描述信息:

    +++
    description = "Where you should come to find my homepage updates and stuff"
    +++
    

    themes/hugo-theme-docdock/layouts/partials/original/head.htmlthemes/hugo-theme-docdock/layouts/partials/original/custom-head.html文件(简称head.html)中添加<meta>标签:

    <meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
    
  2. 文章关键词(keywords)

    在每篇文章的扉页中设置keywords:

    +++
    keywords = [mysite,mysite keyword,Another useful keyword]
    +++
    

    然后,在<head>标签(head.html)内添加<meta>标签:

    <meta content="{{ delimit .Keywords ", " }}" name="keywords">
    
  3. 文章标题(title)

    每篇文章都应有一个标题,方便搜索引擎收录。Hugo生成的文章会在<head>标签内自动添加<title>标签。若无,在文章模板的<head>标签内添加<title>标签。

    <title>{{ $isHomePage := eq .Title .Site.Title }}{{ .Title }}{{ if eq $isHomePage false }} - {{ .Site.Title }}{{ end }}</title>
    

    也可以在<meta>标签中添加标题标签。

    <meta content="{{ $isHomePage := eq .Title .Site.Title }}{{ .Title }}{{ if eq $isHomePage false }} - {{ .Site.Title }}{{ end }}" property="og:title">
    

2、Google搜索优化

  1. 进入Google Search Console

    打开Google网站站长,点击“SEARCH CONSOLE ”进入:

    image-20200117164644387

    • 网域:需要验证DNS,因此只适用于个人域名
    • 网址前缀:Pages网站只能选择此方法,输入完整网址后,点击“继续”按钮
  2. 验证所有权

    进入验证页面:

    image-20200117170542353

    要求下载一个html文件(google××××.html),将这个html文件保存到hugo站点根目录内的static子目录,提交推送到被验证网站后,回到验证页面点击“验证”。

  3. 资源

    验证成功后,点击“前往资源页面”:

    image-20200117170730185

    点”索引”下的”站点地图”,

    image-20200117171415848

    在”添加新的站点地图”处输入被验证网站的sitemap(自动生成,存放在网站根路径):

    image-20200117172801027

    等待一天左右就可以搜到了!

3、baidu搜索优化

打开百度搜索资源平台的链接提交,输入网址:

image-20200122001242571

提交后,百度不保证一定能够收录您提交的链接。

点击“用户中心” — “站点管理” — “添加网站”时,或使用站长工具需要实名认证。

和Google不一样,百度不容许以子目录的方式提交子站点,https://xxx.github.io/xxx/`的形式就不能直接提交了,只能在提交sitemap文件时,提交多个sitemap文件。这样也能勉强让百度收录。

十一、脑图

Hugo除了能够制作个人博客网站外,还能制作脑图(mindmap,又称思维导图)和HTML格式的幻灯片(slide)。

1、Hugo制作脑图

参阅:Hugo中使用思维导图。此方法尚未实现。

想要在blog中插入脑图,之前一直都在使用插入图片的方法,这样非常不优雅。

基于百度的kityminder修改为适用于Hugo:

  1. 这里下载插件

    • kity.min.js:svg(可缩放矢量图)渲染库,放入hugo目录的**static/js/**中
    • kityminder.core.min.js:脑图渲染库,放入hugo目录的**static/js/**中
    • mindmap.jsmindmap.min.js:将li标签(Markdown的列表)转换成脑图需要的json文件,放入hugo目录的**static/js/**中
    • mindmap.css:脑图渲染库,放入hugo目录的**static/css/**中
  2. 在线引用jquery.min.js插件

    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    

    通常情况下,主题已具备,不需要另外准备。

  3. 这里下载kityminder.css,改名为kityminder.core.css,放入hugo目录的**static/css/**中

  4. 引用文件

    在模板的head标签(themes/hugo-theme-docdock/layouts/partials/original/head.html)内引用文件:

    <link href="{{.Site.BaseURL}}css/kityminder.core.css" rel="stylesheet">
    <link href="{{.Site.BaseURL}}css/mindmap.css" rel="stylesheet">
    <script src="{{.Site.BaseURL}}js/kity.min.js"></script>
    <script src="{{.Site.BaseURL}}js/kityminder.core.min.js"></script>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script></script>
    <script src="{{.Site.BaseURL}}js/mindmap.js"></script>
    
    • jquery.slim.min.js导致TOC失败,删除
  5. 使用shortcodes(简码)将Markdown渲染为html

    在hugo目录layouts/shortcodes下新建一个 html(例如为mind.html):

    <div id="{{ .Get 0 }}" class="mindmap">
        {{- .Inner -}}
    </div>
    

    这个shortcodes非常简单,就是将其中的内容渲染出来套个div标签,加上mindmap的类名。

  6. 然后在Markdwon中添加文本

    { {< mind mindid >}}
    - 根目录
        - 一级目录1
            - 二级目录1
            - 二级目录2
        - 一级目录2
    { {< /mind >}}
    

    凡事都不会一帆风顺。Hugo渲染后无显示结果

    • 首先,在模板的head标签内引用文件后,导致页面的TOC失效,删除引用jquery.slim.min.js,TOC恢复正常
    • 然后,查看控制台(Console)发现是mindmap.js触发错误:Uncaught TypeError: Cannot read property ‘childNodes’ of undefined
  • 问题尚未解决(放弃)

2、Hugo引用脑图

通过Hugo制作的脑图,实现的功能和样式单一。现改变思路:Hugo引用脑图的HTML文件。此方法的脑图的功能和样式由制作工具决定,缺点是转换的HTML文件容量比较大。

  • 首先,通过专用工具(例如mindmanager)制作脑图,脑图导出为HTML文件

    下载mindmanager版本,搜索注册码(目前可用:MP20-345-DP56-7778-919A)破解。制作脑图后,“另存为”时选择保存类型为HTML5交互式导图(*.html)。

  • 然后,Hugo引用脑图导出的HTML文件

    假设脑图的HTML文件为Git.html,必须存放在static目录内。避免被Hugo渲染。否则,Hugo渲染脑图文件时会触发错误(命令中存在“-”字符):

    Rebuild failed: “E:\xxx\xxx.html:16:1”: template: __E:\xxx\xxx.htmlHTML:16: unexpected bad character U+002D ‘-’ in command

    16 < script id="mmap-config” type="text/plain">{{mmap-config}}< /script >

    • 方法一:使用button简码跳转到指定页面显示脑图

      优点是布局简洁且不影响当前页面的加载速度(需要时,才点击按钮,在本页面显示脑图的HTML文件内容。缺点是覆盖了本页面(来回切换页面)。

      button简码默认在本页跳转到指定页面(onclick="location.href='{{.}}'"),修改后(onclick="window.open('{{.}}')"),可在新页跳转到指定页面。

      docDock主题的button简码(button.html)的源代码为:

      <!-- Boutton -->
      {{ with .Get "align" }}<center>{{end}}
      <button class="btn  {{with .Get "theme"}}btn-{{.}}{{else}}btn-primary{{end}}" type="button" {{ with .Get "href" }} onclick="location.href='{{.}}'"{{end}} >{{.Inner}}</button>
      {{ with .Get "align" }}</center>{{end}}
      <!-- Boutton -->
      
      • href="url”:url可为任意文件类型。若为本地文件时,url路径相对于static目录。

      在Markdown文件中使用button简码:

      { {< button href="/images/Git.html" >}} Git的思维导图 { {< /button >}}
      
    • 方法二:使用<iframe>框架跳转到子页面显示脑图

      优点是在当前页面显示另一个HTML页面(又称外部页面、子页面)的内容且能自定义CSS;缺点是脑图文件过大,严重影响当前页面的加载速度。

      1. 定义一个<iframe>框架

        在当前页面嵌套子页面:

        <iframe id="xxx" name="xxx" frameborder="0" style="display:block;width:100%; height:545px;" src="url"></iframe>
        
        • id或name=“xxx”:区分不同的子页面

        • frameborder="0|1”:0表示无边框;1表示有边框

        • style="CSS”:设置子页面的CSS

          width为子页面的宽,height为子页面的高;可以使用百分比,会自适应父容器的百分比,也可以使用固定的行高列宽:100px,60px。

        • src="url”:设置子页面的关联url(链接地址),url可为任意文件类型

        • scrolling="auto|yes|no”:是否显示子页面滚动条,缺省时值为auto

      2. 若只打算在Markdown文件中显示脑图

        需要在config.toml文件中开启unsafe = true,url输入脑图的相对路径(相对于当前的Markdown文件)。

      3. 若打算在Hugo(个人博客)中显示脑图

        1. 新建(<iframe>框架修改为)一个简码文件(存入layouts/shortcodes目录,假设为mind.html):

          <iframe id="{{ .Get 0 }}" name="{{ .Get 0 }}" frameborder="0" style="display:block;width:100%; height:545px;" src="{{ .Get 1 }}">
          </iframe>
          
          • {{ .Get 0 }}:0表示使用第一个传入参数;1表示使用第二个传入参数
        2. Markdown文件使用简码:

          { {< mind mindid "/images/Git.html" >}}
          
          • 第一个参数是简码名称、第二个参数传递给id和name、第三个参数传递给src(脑图的HTML文件,存入static目录,路径相对于static目录)

十二、幻灯片

幻灯片,是使用全屏来显示Markdown文件内容的页面。

Markdown文件内容可以呈现为全屏的manifest.js演示文稿。manifest.js使您可以使用HTML创建漂亮的交互式幻灯片。参阅:PRESENT A SLIDE

  1. 在Markdown文件的扉页中设置type="slide"[revealOptions]

    +++
    title = "Test slide"     # Markdown文件的标题
    type="slide"             # Markdown文件的类型:slide(幻灯片)
    theme = "league"         # revel.js框架(幻灯片)的主题
    [revealOptions]
    transition= 'concave'    # 幻灯片过渡类型值:none|fade|slide|convex|concave|zoom
    controls= true           # 显示幻灯片的控制箭头
    progress= true           # 显示幻灯片的演示进度条
    history= true            # 将每个幻灯片的更改记入浏览器历史记录
    center= true             # 滑块垂直对中
    +++
    
    • 参数参阅:https://github.com/hakimel/reveal.js/#configuration
  2. 幻灯片定界符

    在Markdown文件中为幻灯片演示文稿创建内容时,使用定界符区分每一张幻灯片:

    • 水平幻灯片的定界符:3个减号---

    • 垂直幻灯片的定界符:3个下划线___

      通常,垂直幻灯片用于在顶层水平幻灯片下方显示信息。

  3. 创建幻灯片内容

    使用#标记标题的层级

    # In the morning
       
    

Getting up

  • Turn off alarm
  • Get out of bed

Breakfast

  • Eat eggs
  • Drink coffee

In the evening


Dinner

  • Eat spaghetti
  • Drink wine

Going to sleep

  • Get in bed
  • Count sheep

4. 使用HTML语法实现各种功能

reveal.js控制幻灯片的行为和样式,Markdown控制幻灯片的内容,HTML语法控制幻灯片的多媒体功能。Hugo的设置(例如:**config.toml**内的设置、简码等)在幻灯片中无效,可直接使用HTML语法扩展幻灯片的多样化功能。



## 参考资料

> 官网:
>
> - [Hugo官网](https://gohugo.io/)
> - [Hugo官网中文镜像](https://s0gohugo0io.icopy.site/ )
> - [Hugo官方教程(英文)Hugo Docs](https://gohugo.io/documentation/)
> - [HUGO LEARN THEME](https://learn.netlify.com/en/)
> - [Travis CI官方文档](https://docs.travis-ci.com/)
> - [Netlify官方文档](https://docs.netlify.com/)
>
> 个人博客:
>
> - [Tutorials for the Hugo static site generator](https://kodify.net/hugo-static-site-tutorials/)
> - [Blog养成记](https://orianna-zzo.github.io/series/blog养成记/)
> - [本站引用图片的“顺滑”流程](https://rayhy.com/blog/20190301-本站引用图片的顺滑流程/)
> - [Hugo静态网站生成器中文教程](http://nanshu.wang/post/2015-01-31/)
> - [Hugo 搭建博客实践](https://creaink.github.io/post/Devtools/Hugo/Hugo-intro.html)
> - [使用Hugo建个网站](https://lyzhang.me/post/make_a_blog_with_hugo/)
> - [hugo中文帮助文档](https://hugo.aiaide.com/)
> - [Hugo学习笔记](https://skyao.io/learning-hugo/)
> - [苏洋博客](https://soulteary.com/)
> - [静态网站构建手册-使用Hugo构建个人博客](https://jimmysong.io/hugo-handbook/)

## 操作环境

> - 操作系统:Win 10
> - Hugo工具:hugo_0.61.0_Windows-64bit
> - Hugo主题:hugo-theme-docdock

感谢您的赞赏支持:

Copyright © 2020 komantao. All Rights Reserved.