material-docs主题

主题介绍

material-docs官网:

http://themes.gohugo.io/theme/material-docs/

这个主题非常适合用于文档,左边的导航栏支持多级菜单,样式也简单美观,而且对移动设备支持很好。

我主要用它来做学习笔记,替代之前长期使用的gitbook(gitbook的问题主要是本地生成内容速度太慢,而gitbook官方网站访问速度又不好)。当前这个hugo学习笔记用的就是此主题。

准备工作

Fork并定制

material-docs的github仓库已经有很长时间没有更新,基本停留在两年前。从实践中看,有很多问题,对新的hugo版本的支持也很不好。

因此,采用fork的方式,建立自己的仓库:

https://github.com/skyao/hugo-material-docs

期间做了很多了更新:

  • PR那边有很多已经被发现而且修订提交的问题,但是一直没有merge,我手工合并到自己的仓库
  • 弃用google统计,改用百度统计,具体做法和 academic 中的做法是一样的
  • 同样进行了本地加速,参照 academic 中的做法
  • 修改了layout文件,主要是删除了版权申明,作者信息,下载修改为意见反馈,现在的版面非常的干净,没有任何多余的东西
  • 汉化,没有做标准的i18n,因为是给自己定制,因此直接在layout文件中修改为中文。
  • 修改了exampleSite的内容,删除原有的页面和图片,改为学习笔记的常见页面如介绍,安装等,方便后续使用
  • 提供了简单的gitbook模版文件支持,可以生成一个简单带有文字说明和链接的页面,以便将原有gitbook用户导流到新的hugo笔记

使用方式

以新建一个学习笔记为例,详细描述需要的步骤和命令。

新建学习笔记

  • 在github中新建仓库,名为 learning-cilium的Cilium学习笔记

  • 在本地新建一个hugo站点

  1. hugo new site learning-cilium
  2. cd learning-cilium
  • 下载更新主题的脚本并执行
  1. wget https://raw.githubusercontent.com/skyao/hugo-material-docs/master/exampleSite/update_theme.sh
  2. chmod +x update_theme.sh
  3. ./update_theme.sh

命令完成之后,themes/hugo-material-docs目录下就有我们自己的主题文件内容。

  • exampleSite目录复制需要的站点初始化文件到新站点
  1. cd themes/hugo-material-docs
  2. # 复制config.toml文件
  3. cp config.toml ../../../config.toml
  4. # 复制content目录和static目录
  5. cp -r content/ ../../../content/
  6. cp -r static/ ../../../static/
  7. # 复制gitignore文件
  8. cp gitignore-example ../../../.gitignore
  • 修改新站点的config.toml文件

以下内容必须修改:

  1. title = "学习笔记"
  2. repo_url = "https://github.com/skyao/learning-hugo"
  • 此时已经开始运行hogo server命令来查看新站点的内容了

  • 继续修改站点内容

主要是content目录下的index.md以及installationintroduction两个目录下的文件,也可以继续修改config.toml文件,可以从浏览器上实时看到修改后的效果

  • 复制content/index.md文件到根目录下,作为README.md文件
  1. cp content/index.md README.md

然后删除文件头部的hugo元数据。

  • 提交第一个初始版本到github
  1. git init
  2. # 检查一下要提交的文件是否如预期
  3. git status
  4. git add -A
  5. git commit -m "first version"
  6. git remote add origin git@github.com:skyao/learning-cilium.git
  7. git push -u origin master
  • 和gitbook共存

由于原来的读书笔记是gitbook格式,因此为了让原来gitbook那边的老用户有机会找回到新的基于hugo的笔记,需要在gitbook那边好歹留一个简单页面说明。

为此,保留gitbook的最少文件,如book.jsonREADME.md即可。是的,SUMMARY.md文件都是可以不用的,而且README.md文件本来也可以用来作为在github仓库的首页文档,因此实质上只是多了一个book.json文件。

内容示意如下:

  1. {
  2. "gitbook": ">=3.2.0",
  3. "description": "Hugo学习笔记",
  4. "language": "zh-hans",
  5. "author": "敖小剑",
  6. "extension": null,
  7. "generator": "site",
  8. "links" : {
  9. "sidebar" : {
  10. "敖小剑的博客": "https://skyao.io"
  11. }
  12. },
  13. "structure" : {
  14. },
  15. "plugins": [
  16. "-search",
  17. "-sharing"
  18. ],
  19. "pluginsConfig": {
  20. },
  21. "title": "Hugo学习笔记",
  22. "variables": {
  23. }
  24. }
  • 最后,准备好.gitignore文件
  1. .*
  2. !.gitignore
  3. _book/
  4. node_modules/
  5. public/
  6. themes/
  7. update_theme.sh

扩展和增强

三次菜单和自动隐藏

默认,material-docs主题的主菜单,只显示两层。修改代码之后可以支持三层菜单显示,以应对笔记内容比较多的情况。

但是也引发了另外一个问题,内容比较多的笔记,三级菜单一起展开,主菜单项的内容实在太多了。而且material-docs主题的主菜单是每个页面都嵌入的,不是iframe那种,导致菜单项比很多页面的实际内容都要多。因此考虑实现菜单项的自动隐藏,当菜单项(或者子项)没有被打开时,就只打开一级菜单。

需要修改hugo-material-docs/layouts/partials/nav.html文件:

  1. {{ $currentNode := . }}
  2. {{ range .Site.Menus.main.ByWeight }}
  3. {{ $.Scratch.Set "currentMenuEntry" . }}
  4. <li>
  5. {{ if .HasChildren }}
  6. {{ partial "nav_link" $currentNode }}
  7. {{ if hasPrefix ( $.Permalink | relURL | printf "%s" ) .URL }}
  8. <ul>
  9. {{ range .Children }}
  10. {{ $.Scratch.Set "currentMenuEntry" . }}
  11. <li>
  12. {{ if .HasChildren }}
  13. {{ partial "nav_link" $currentNode }}
  14. {{ if hasPrefix ( $.Permalink | relURL | printf "%s" ) .URL }}
  15. <ul>
  16. {{ range .Children }}
  17. {{ $.Scratch.Set "currentMenuEntry" . }}
  18. {{ partial "nav_link" $currentNode }}
  19. {{ end }}
  20. </ul>
  21. {{ end }}
  22. {{ else }}
  23. {{ partial "nav_link" $currentNode }}
  24. {{ end }}
  25. </li>
  26. {{ end }}
  27. </ul>
  28. {{ end }}
  29. {{ else }}
  30. {{ partial "nav_link" $currentNode }}
  31. {{ end }}
  32. </li>
  33. {{ end }}

因为不熟悉代码,暂时通过写笨代码的方式实现了第三级菜单,最好是能用递归的方式,这样就可以实现无限级的菜单了。{{ if hasPrefix ( $.Permalink | relURL | printf "%s" ) .URL }} 用来判断是当前菜单或者当前菜单的子项是否打开,这个判断在第二级和第三级菜单中执行。

发现并修复的问题

主菜单上当前页的H2菜单不显示

这个主题有个特性,可以在主菜单中,在当前页面下会自动增加所有H2 titile作为子菜单。这个特性相当于自动增加了一级菜单,对于内容长的页面很有用。

发现,如果baseURL是完全域名如”https://skyao.io/"或者"http://localhost:1313"时,工作正常。但是,如果baseURL是"https://skyao.io/learning-hugo/"或者"http://localhost:1313/learning-hugo/"这样带有子目录时,就会失效。

检查发现,是themes/hugo-material-docs/layouts/partials下的nav_link.html中,这段代码判断错误:

  1. {{ $currentMenuEntry := .Scratch.Get "currentMenuEntry" }}
  2. {{ $isCurrent := eq .Permalink ($currentMenuEntry.URL | absURL | printf "%s") }}

这里判断菜单项是否是当前页面时,判断的方式是将当前页面的实际URL Permalink(内容如https://skyao.io/learning-hugo/installation/seo/) 和菜单项URL地址$currentMenuEntry.URL(内容如/installation/seo/)进行比较,如果一致则表示是当前页面。为了得到菜单项URL地址的绝对路径,需要使用absURL函数,如($currentMenuEntry.URL | absURL | printf "%s")

但是这里的absURL函数输出结果居然是不正确的!baseURL为https://skyao.io/learning-hugo/ ,参数为 /installation/seo/时,函数absURL的结果居然是https://skyao.io/installation/seo/learning-hugo/子目录消失了。

修改方式为通过获取Permalink地址的相对路径,然后和$currentMenuEntry.URL比较,这样就避开了absURL函数的问题:

  1. {{ $currentMenuEntry := .Scratch.Get "currentMenuEntry" }}
  2. {{ $isCurrent := eq ( .Permalink | relURL | printf "%s" ) $currentMenuEntry.URL }}

这里还发现一个要注意的地方,在config.toml文件中,主菜单项的配置,url一定要用”/“开头,即:

  1. [[menu.main]]
  2. name = "安装"
  3. identifier = "installation"
  4. url = "/installation/" # 这里一定不要设置为"installation/"
  5. weight = 200

测试验证OK!

Index页面变成空白页

这个问题来历有些特别,material-docs主题因为太长时间没有维护,所以和新版本的hugo有非常大的差异。

体现在_index.md文件的使用上,material-docs主题的exampleSite,跑在新版本的hugo上,各个页面都直接报错”404”!看public目录发现,content目录下的除index.md文件外,其他内容包括子目录都没有被处理。

解决的方式就是在每个目录(包括根目录)下放置一个_index.md文件。

但是,首页会偶尔出现问题,/这个页面偶尔会出现空白,没有内容,有时有能正常显示index.md文件的内容。没有规律,有时发生。

最后,解决的方式是:

  • 修改主题布局文件 hugo-material-docs/layouts/index.html
  1. {{ range where .Site.Pages "Type" "index" }}
  2. <h1>{{ .Title }} {{ if .IsDraft }} (Draft){{ end }}</h1>
  3. {{ .Content }}
  4. {{ end }}

删除这里的range循环,不要这个功能(可以自动从其他type为index的页面提取Content然后聚合在最后生成的index.html文件里面,不好用):

  1. <h1>{{ .Title }} {{ if .IsDraft }} (Draft){{ end }}</h1>
  2. {{ .Content }}

  • 在根目录下只放置一个_index.md文件,原来index.md的内容都放在这个文件里面。或者说,将index.md文件改名为”_index.md”。其他目录下还是同时放index.md_index.md文件。

上一页和下一页的准确性

页面排序有两个位置:

  • 左边的主菜单栏:这个是通过config.toml文件中定义的main menu的weight来决定,配合每个页面的weight。

注意这时单个目录下的子页面的排序是在当前目录下进行。

  • 每个页面的底部有上一页和下一页

默认是按照时间(即每个页面上的data信息)排序,所以和前面用weight排序的结果就会很有大出入。

因此,需要修改模板文件将排序方式修改为按照weight排序。

其次,这里的排序是全局(即所有页面)进行排序。和前面每个目录下单独排序不一样,因此要让页面的上一页和下一页显示的内容和主菜单一致,就必须保证两个方法排序的一致性。

推荐的方式是:子目录之间分配好weight的范围,比如a目录是100-199,b目录是200-299。然后a目录下的页面的weight就一定要取值在100-199中,b目录下的页面的weight就一定要取值在200-299。

待解决的问题

主题已经不再更新

作者已经消失,一年多没有更新,包括PR也没有人合并。

随着hugo的版本越来越新,这个主题的问题也越来越多。希望后面有人继续维护这个主题,或者有类似的替代gitbook适合编写学习笔记类的主题出现。