本文档介绍 jijian 主题的模板结构、继承关系和自定义方法,帮助你深入理解主题的工作原理并进行定制开发。


模板结构概览

jijian 主题采用 Hugo 标准的模板层次结构:

themes/jijian/layouts/
├── _default/              # 默认模板
│   ├── baseof.html        # 基础模板
│   ├── blank.html         # 空白页面模板
│   ├── single.html        # 单篇文章页面
│   ├── list.html          # 列表页面
│   ├── terms.html         # 标签/分类归档页
│   ├── archives.html      # 时间线归档页
│   ├── search.html        # 搜索页面
│   └── rss.xml            # RSS 订阅源
├── page/                  # 特定页面模板
│   └── password.html      # 密码保护页面
├── partials/              # 部分模板
│   ├── head.html          # HTML 头部
│   ├── header.html        # 页面顶部导航
│   ├── footer.html        # 页面底部
│   ├── card.html          # 名片模式首页
│   ├── portal.html        # 门户模式首页
│   ├── cover.html         # 文章封面图
│   ├── toc.html           # 目录
│   ├── post_meta.html     # 文章元信息
│   ├── post_nav_links.html # 上下篇导航
│   ├── breadcrumbs.html   # 面包屑导航
│   ├── social_icons.html  # 社交图标
│   ├── share_icons.html   # 分享按钮
│   ├── comments.html      # 评论系统
│   ├── edit_post.html     # 编辑文章链接
│   ├── translation_list.html # 多语言列表
│   ├── author.html        # 作者信息
│   ├── anchored_headings.html # 带锚点的标题
│   ├── custom_wordcount.html # 自定义字数统计
│   ├── svg.html           # SVG 图标库
│   ├── extend_head.html   # 头部扩展区域
│   ├── extend_footer.html # 底部扩展区域
│   └── templates/         # SEO 模板
│       ├── opengraph.html # Open Graph 元数据
│       ├── x_cards.html   # Twitter/X Cards 元数据
│       └── schema_json.html # Schema.org 结构化数据
├── shortcodes/            # 短代码
│   ├── bilibili.html      # 哔哩哔哩视频
│   ├── figure.html        # 增强版图片
│   ├── collapse.html      # 可折叠内容
│   ├── vertical.html      # 竖排文本
│   ├── inTextImg.html     # 行内图片
│   └── rawhtml.html       # 原始 HTML
├── 404.html               # 404 错误页面
└── robots.txt             # 搜索引擎爬虫配置

模板继承关系

基础模板层次

baseof.html (基础模板)
├── head.html (头部)
│   ├── Meta 信息
│   ├── CSS 样式
│   ├── Favicon
│   ├── 主题脚本
│   └── templates/
│       ├── opengraph.html
│       ├── x_cards.html
│       └── schema_json.html
├── header.html (导航)
│   ├── Logo
│   ├── 主题切换
│   ├── 语言切换
│   └── 导航菜单
├── main block (主内容区)
│   ├── single.html (文章页)
│   │   ├── breadcrumbs.html
│   │   ├── toc.html
│   │   ├── post_meta.html
│   │   ├── translation_list.html
│   │   ├── edit_post.html
│   │   ├── share_icons.html
│   │   └── comments.html
│   ├── list.html (列表页)
│   │   ├── portal.html (门户模式)
│   │   └── card.html (名片模式)
│   ├── archives.html (归档页)
│   ├── search.html (搜索页)
│   └── terms.html (标签/分类页)
└── footer.html (页脚)
    ├── 版权信息
    ├── 返回顶部
    ├── 主题切换
    └── extend_footer.html

核心模板详解

baseof.html - 基础模板

所有页面的基础骨架,定义了整体 HTML 结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}" dir="auto">
<head>
    {{- partial "head.html" . -}}
</head>
<body class="{{ if .IsHome }}home{{ end }}">
    {{- partial "header.html" . -}}
    <main class="main">
        {{- block "main" . }}{{- end }}
    </main>
    {{- partial "footer.html" . -}}
</body>
</html>

关键点

  • 使用 block "main" 定义主内容区块
  • 子模板通过 define "main" 填充内容

single.html - 文章页模板

单篇文章的展示模板:

文章页结构
├── 面包屑导航 (breadcrumbs.html)
├── 文章标题
├── 文章元信息 (post_meta.html)
│   ├── 发布日期
│   ├── 阅读时长
│   ├── 字数统计
│   └── 作者信息
├── 多语言翻译列表 (translation_list.html)
├── 目录 (toc.html)
├── 文章正文
├── 编辑链接 (edit_post.html)
├── 分享按钮 (share_icons.html)
├── 上下篇导航 (post_nav_links.html)
└── 评论区 (comments.html)

list.html - 列表页模板

文章列表的展示模板,支持三种首页模式:

列表页结构
├── 首页模式判断
│   ├── cardMode → card.html
│   ├── portalMode → portal.html
│   └── 默认 → 文章列表
├── 分页导航
└── 文章条目
    ├── 封面图(可选)
    ├── 标题
    ├── 日期
    └── 摘要

head.html - 头部模板

包含所有 <head> 内的内容:

head.html 结构
├── Meta 标签
│   ├── charset
│   ├── viewport
│   ├── description
│   └── keywords
├── 标题
├── Favicon
├── CSS 样式
│   ├── 主样式
│   ├── 代码高亮
│   └── 扩展样式
├── 主题切换脚本
├── SEO 模板
│   ├── opengraph.html
│   ├── x_cards.html
│   └── schema_json.html
└── extend_head.html (扩展区域)

自定义模板

方法一:覆盖主题模板

在站点根目录的 layouts 文件夹中创建同名模板文件,会自动覆盖主题模板:

your-site/
├── layouts/
│   ├── _default/
│   │   └── single.html    # 覆盖主题的 single.html
│   └── partials/
│       └── footer.html    # 覆盖主题的 footer.html
└── themes/
    └── jijian/
        └── layouts/
            ├── _default/
            │   └── single.html
            └── partials/
                └── footer.html

方法二:使用扩展模板

主题提供了 extend_head.htmlextend_footer.html 扩展点:

添加自定义 CSS/JS

创建 layouts/partials/extend_head.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!-- 自定义样式 -->
<style>
.my-custom-class {
    color: red;
}
</style>

<!-- 引入外部 CSS -->
<link rel="stylesheet" href="https://example.com/style.css">

<!-- 自定义脚本 -->
<script>
console.log('Custom script loaded');
</script>

创建 layouts/partials/extend_footer.html

1
2
3
4
5
6
7
<!-- 页脚自定义内容 -->
<div class="custom-footer">
    自定义页脚内容
</div>

<!-- 引入外部 JS -->
<script src="https://example.com/script.js"></script>

方法三:创建新的部分模板

创建自定义部分模板并在主模板中引用:

  1. 创建 layouts/partials/my-component.html
1
2
3
<div class="my-component">
    {{ .Text }}
</div>
  1. 在其他模板中引用:
1
{{ partial "my-component.html" (dict "Text" "Hello World") }}

页面类型模板

归档页 (archives.html)

时间线归档页面,按年份和月份展示所有文章:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{{ define "main" }}
<div class="archive">
    <h1>{{ .Title }}</h1>
    {{ range .Site.RegularPages.GroupByDate "2006" }}
    <div class="archive-year">
        <h2>{{ .Key }}</h2>
        {{ range .Pages.GroupByDate "01月" }}
        <div class="archive-month">
            <h3>{{ .Key }}</h3>
            {{ range .Pages }}
            <article>
                <time>{{ .Date.Format "01-02" }}</time>
                <a href="{{ .Permalink }}">{{ .Title }}</a>
            </article>
            {{ end }}
        </div>
        {{ end }}
    </div>
    {{ end }}
</div>
{{ end }}

访问路径/archives/

配置方法

1
2
3
4
5
6
menu:
  main:
    - identifier: archives
      name: 归档
      url: /archives/
      weight: 20

搜索页 (search.html)

Pagefind 搜索页面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{{ define "main" }}
<div class="search">
    <h1>{{ .Title }}</h1>
    <div id="search"></div>
    <link href="/pagefind/pagefind-ui.css" rel="stylesheet">
    <script src="/pagefind/pagefind-ui.js"></script>
    <script>
    new PagefindUI({
        element: "#search",
        showSubResults: true,
        showImages: false
    });
    </script>
</div>
{{ end }}

访问路径/search/

构建搜索索引

1
hugo && npx pagefind --site public --output-path public/pagefind

标签/分类页 (terms.html)

标签和分类的列表页:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{{ define "main" }}
<div class="terms">
    <h1>{{ .Title }}</h1>
    <ul>
    {{ range .Pages }}
        <li>
            <a href="{{ .Permalink }}">{{ .Title }}</a>
            <span>{{ len .Pages }} 篇</span>
        </li>
    {{ end }}
    </ul>
</div>
{{ end }}

访问路径

  • 标签:/tags/
  • 分类:/categories/

404 页面

错误页面模板:

1
2
3
4
5
6
7
{{ define "main" }}
<div class="error-404">
    <h1>404</h1>
    <p>页面未找到</p>
    <a href="{{ .Site.BaseURL }}">返回首页</a>
</div>
{{ end }}

模板变量

全局变量

在模板中可使用的 Hugo 变量:

变量 说明
.Site 站点对象
.Page 当前页面对象
.Title 页面标题
.Content 页面内容
.Summary 页面摘要
.Date 发布日期
.Lastmod 最后修改日期
.Permalink 页面永久链接
.RelPermalink 页面相对链接
.Params 页面参数
.Site.Params 站点参数

常用方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 获取配置参数 -->
{{ .Site.Params.author }}

<!-- 条件判断 -->
{{ if .Site.Params.ShowToc }}
    {{ partial "toc.html" . }}
{{ end }}

<!-- 循环 -->
{{ range .Site.Menus.main }}
    <a href="{{ .URL }}">{{ .Name }}</a>
{{ end }}

<!-- 日期格式化 -->
{{ .Date.Format "2006年01月02日" }}

<!-- 字数统计 -->
{{ .WordCount }}

<!-- 阅读时长 -->
{{ .ReadingTime }}

部分模板详解

toc.html - 目录模板

生成文章目录,支持多种显示模式:

配置参数

1
2
3
4
5
6
7
params:
  ShowToc: true
  TocOpen: true
  TocPosition: auto      # left / right / auto
  tocHover: true         # 悬浮模式
  tocHoverDelay: 200     # 悬浮延迟
  tocHideDelay: 2000     # 隐藏延迟

social_icons.html - 社交图标模板

显示社交图标,支持悬浮二维码:

配置示例

1
2
3
4
5
6
params:
  socialIcons:
    - name: bilibili
      url: https://space.bilibili.com/xxxxxx
      title: 哔哩哔哩
      hoverImage: /img/bilibili-qr.png

comments.html - 评论模板

集成 Giscus 评论系统:

配置示例

1
2
3
4
5
6
7
params:
  comments: true
  giscus:
    repo: "user/repo"
    repoId: "R_xxxxx"
    category: "Announcements"
    categoryId: "DIC_xxxxx"

自定义示例

添加文章底部广告

创建 layouts/partials/extend_footer.html

1
2
3
4
5
{{ if .IsPage }}
<div class="post-ad">
    <p>如果觉得文章有帮助,请支持作者 ❤️</p>
</div>
{{ end }}

添加阅读进度条

创建 layouts/partials/extend_head.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<style>
.reading-progress {
    position: fixed;
    top: 0;
    left: 0;
    width: 0%;
    height: 3px;
    background: linear-gradient(to right, #4CAF50, #2196F3);
    z-index: 9999;
    transition: width 0.1s ease;
}
</style>

创建 layouts/partials/extend_footer.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{ if .IsPage }}
<div class="reading-progress" id="readingProgress"></div>
<script>
window.addEventListener('scroll', function() {
    var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
    var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
    var scrolled = (winScroll / height) * 100;
    document.getElementById("readingProgress").style.width = scrolled + "%";
});
</script>
{{ end }}

自定义文章归档样式

创建 layouts/_default/archives.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{{ define "main" }}
<div class="archive-custom">
    <h1>文章归档</h1>
    {{ range .Site.RegularPages.GroupByDate "2006" }}
    <div class="year-group">
        <h2 class="year-title">{{ .Key }}年</h2>
        {{ range .Pages }}
        <article class="archive-item">
            <time datetime="{{ .Date.Format "2006-01-02" }}">
                {{ .Date.Format "01-02" }}
            </time>
            <a href="{{ .Permalink }}">{{ .Title }}</a>
            <span class="category">
                {{ range .Params.categories }}{{ . }}{{ end }}
            </span>
        </article>
        {{ end }}
    </div>
    {{ end }}
</div>
{{ end }}