【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
上篇 blog
【Ubuntu】【Hugo】搭建私人博客:文章目录(一)
分析了文章的目录 TOC 功能,并分析了 PaperMod 主题下的默认 toc.html 模板,其中分析完了【提取标题】,下面继续【<details> 可折叠容器】的分析
搭建私人博客
继续看 【<details> 可折叠容器】

{``{if (.Param "TocOpen") }} open{``{ end }}:Hugo 模板语法,作用是如果在文章的标题开头写了TocOpen = true,比如

或者在hugo.toml配置文件中,加入了这个全局配置项,那么 Hugo 在渲染这里时,会自动加上open这个属性,这里就会变成<details open>,表示页面一打开,目录就展开(本来默认是收起的)accesskey="c":设置一个快捷键 ,在大多数浏览器中,按下Alt + C(Windows)就能聚焦到这个<summary>上titile="...":鼠标悬停在这里时,显示的提示文字,告诉用户这里可以使用快捷键
OK,看第三部分:选择 TOC 的生成方式

如果在文章,或 hugo.toml 的全局配置中,启用了配置项 UseHugoToc = true,就会直接使用 Hugo 内置的 .TableOfContents,更简单,但样式(风格)和 PaperMod 不一定是匹配的,否则就执行下面复杂的手动解析过程(PaperMod 默认方式,样式更统一)
第四部分:手动构建嵌套列表(核心难点)

这部分很复杂,因为 Hugo 模板语言没有循环栈,或者递归函数,只能用模拟栈的方式去处理嵌套层级
这里要区分下 Hugo 模板语言和 Go 语言,虽然 Hugo 是用 Go 语言开发的,但在比如 toc.html 这样模板文件上,写的不是 Go 代码,而是 Hugo 的模板语言(基于 Go 的 text/template 和 html/template)
这个模板语言有如下特点:
- 支持变量,
if条件判断,range循环等 - 不支持函数定义,递归,复杂数据结构的操作(比如栈,队列等)
- 不能直接把字符串转成整数,所以这里需要用
len (seq "$headerLevel")这样的技巧

简单来说,Hugo 模板是一个受限,表达能力有限的 DSL(领域特定语言),和完整的 Go 语言有很大区别
OK,继续看这段代码
go
{{- $largest := 6 -}}
{{- range $headers -}}
{{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}}
{{- $headerLevel := len (seq $headerLevel) -}}
{{- if lt $headerLevel $largest -}}
{{- $largest = $headerLevel -}}
这段代码的的含义是,找出最小标题级别,比如(h2 是 2,h3 是 3,则最小是 2),这里 $largest 实际上是最小的标题数字(对应最高层级,比如 h2 在标题上比 h3 要大)
这里可能有人会有疑问,如果按这么说的话,那最小的不都是 h1 吗?
答案是不一定,因为有人可能不按常理出牌,他可能会跳过 h1 标题,直接用 h2,甚至 h3 标题开头,就比如
md
---
title: "跳过 h1 的文章"
toc: true
---
## 这是 h2 标题
一些内容...
### 这是 h3 子标题
更多内容...
可以看到,这里并没有 h1 标题,所以 PaperMod 的 toc.html 处理中,并不假设必须有 h1 或 h2,而是动态找出所有标题中最小的层级(比如全是 h3/h4,就以 h3 为根),并以这个最小层级作为 TOC 的顶层,所有更深的标题都作为它的子项,相当于把 h3 当做逻辑上的顶级标题来对待,就好像一本书,如果第一章直接从 【1.3 小节】开始(中间没有【1.1 小节】,【1.2 小节】),那就把 【1.3 小节】当成第一章的主标题来列目录,虽然奇怪,但是技术上可行
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog
【Ubuntu】【Hugo】搭建私人博客:文章目录(三)