惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
F
Fortinet All Blogs
Engineering at Meta
Engineering at Meta
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
The Exploit Database - CXSecurity.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
Intezer
P
Privacy & Cybersecurity Law Blog
M
MIT News - Artificial intelligence
MyScale Blog
MyScale Blog
P
Privacy International News Feed
MongoDB | Blog
MongoDB | Blog
Project Zero
Project Zero
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tenable Blog
Security Latest
Security Latest
Stack Overflow Blog
Stack Overflow Blog
L
Lohrmann on Cybersecurity
V
Vulnerabilities – Threatpost
Microsoft Azure Blog
Microsoft Azure Blog
NISL@THU
NISL@THU
T
Threat Research - Cisco Blogs
L
LangChain Blog
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
SecWiki News
SecWiki News
博客园 - 三生石上(FineUI控件)
Forbes - Security
Forbes - Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
GRAHAM CLULEY
K
Kaspersky official blog
W
WeLiveSecurity
A
Arctic Wolf
TaoSecurity Blog
TaoSecurity Blog
Recorded Future
Recorded Future
AI
AI
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Last Week in AI
Last Week in AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
雷峰网
雷峰网
GbyAI
GbyAI
S
SegmentFault 最新的问题
N
News and Events Feed by Topic
C
CXSECURITY Database RSS Feed - CXSecurity.com
Google Online Security Blog
Google Online Security Blog
博客园 - Franky
罗磊的独立博客

Halo - 强大易用的开源建站工具

如何选择最佳网站搭建方案?2026年12款建站工具对比测评! Halo 和 WordPress 对比:开源 CMS 怎么选? Halo 和 Ghost 对比:博客、简报与会员订阅怎么选? Halo 和 Typecho 对比:现代 CMS 还是轻量博客? Halo 和 EmDash 对比:成熟 CMS 还是 Cloudflare 新方案? Halo 和 Hugo 对比:动态 CMS 还是静态网站生成器? Halo 和 Hexo 对比:动态 CMS 还是静态博客? Halo 和 Strapi 对比:全栈建站平台还是 Headless CMS? 在群晖 NAS 上用 Docker 部署 Halo 用 Obsidian 写文章,一键发布到 Halo 域名是什么?新手注册域名前要知道的 7 件事 虚拟主机、VPS 和云服务器有什么区别?建站服务器怎么选 建站需要准备什么?域名、服务器、备案和 SSL 的关系 域名解析:A 记录、CNAME、MX、TXT 分别怎么用 网站备案是什么?个人备案和企业备案有什么不同 SSL 证书和 HTTPS 是什么?免费证书够用吗 CDN 是什么?网站什么时候需要 CDN robots.txt 是什么?新手建站常见配置误区 Sitemap 是什么?它对网站收录有什么作用 SEO 写作基础:标题、描述、URL 和内链怎么做 301 和 302 重定向有什么区别?网站改版怎么降低排名风险 Google Analytics、Umami、Rybbit:流量统计工具怎么选 网站图片优化指南:格式、压缩和懒加载怎么做 网站安全基础:建站后必须做的 6 件事 Halo 如何定时发布文章 Halo 如何接入 Bing Webmaster Tools 支持优惠券和折扣码,Halo 2.25 发布 AI Foundation:给 Halo 插件生态一层 AI 能力底座 Halo 应用市场已支持开发者入驻和创建应用 Halo 如何接入 Google Search Console 如何自动备份 Halo 网站 让 AI Agents 管理 Halo 网站内容 支持商城小程序,Halo 2.24 发布 如何在本地快速体验 Halo 使用 Halo CLI + GitHub Actions 持续部署主题 使用页面缓存插件优化 Halo 网站速度 阿里云 ESA 接入 Halo 配置指南 在本地搭建 Halo 插件开发环境 七牛云 DCDN 接入 Halo 配置指南 如何一键更新 Halo 的主题和插件 又拍云 CDN 接入 Halo 配置指南 腾讯云 EdgeOne 接入 Halo 配置指南 Halo 网站加载慢?8 种原因排查与优化方法 Halo 导入 Word 文档教程:批量导入并保留图片 Rybbit 接入 Halo 教程:开源隐私友好的流量分析工具 Umami 接入 Halo 教程:自托管网站流量统计 在 Halo 中使用 Markdown 写作的 6 种方式 如何从社区版切换到 Halo 付费版
主题开发技巧:为 Halo 网站添加一个知识库样式的页面
Ryan Wang · 2026-04-13 · via Halo - 强大易用的开源建站工具

随着 Halo 站点内容越来越丰富,许多站长会面临一个问题:博客列表页适合展示时效性内容,但对于产品文档、使用教程、常见问题等结构化知识内容来说,用普通博客列表来组织显得太扁平,用户很难快速找到自己需要的内容。

本文介绍如何利用 Halo 的自定义分类模板机制,在不依赖任何额外插件的情况下,为主题添加一套完整的知识库页面,包括知识库入口和子分类文章列表。

实现原理

Halo 支持在 theme.yaml 中为分类(category)注册额外的渲染模板,称为 自定义分类模板。用户在后台管理某个分类时,可以从下拉菜单中选择使用哪套模板来渲染该分类页面。

知识库的层级结构正好与分类树对应:

知识库根分类(使用 category_knowledge_base_root 模板)
├── 入门指南(使用 category_knowledge_base 模板)
│   ├── 文章 A
│   └── 文章 B
└── 进阶使用(使用 category_knowledge_base 模板)
    ├── 文章 C
    └── 文章 D

第一步:在 theme.yaml 注册模板

theme.yamlspec.customTemplates 下注册模板:

spec:
  customTemplates:
    post:
      - name: 知识库
        description: 知识库文章页
        file: post_knowledge_base.html
    category:
      - name: 知识库入口
        description: 知识库入口页,展示所有子分类及文章预览
        file: category_knowledge_base_root.html
      - name: 知识库
        description: 知识库子分类,展示该分类下的文章列表
        file: category_knowledge_base.html

修改完 theme.yaml 后,需要在后台 主题 → 重载主题配置 才能生效。之后在分类设置中就能看到这些模板选项。

第二步:知识库入口页

模板名称:category_knowledge_base_root.html

入口页负责展示整个知识库的全貌:标题、描述、搜索入口,以及所有子分类的卡片网格。

核心 API

  • categoryFinder.getByNames(category.spec.children) — 根据父分类的 children 字段获取子分类列表

  • postFinder.listByCategory(1, 5, subCat.metadata.name) — 获取每个子分类的前 5 篇文章

<!-- Hero 区域:展示知识库标题、描述和搜索入口 -->
<section class="hero">
  <h1 th:text="${category.spec.displayName}"></h1>
  <p
    th:text="${category.spec.description ?: '快速查找所需内容,轻松解决每一个问题。'}"
  ></p>

  <!-- 搜索按钮(需安装 PluginSearchWidget) -->
  <button
    th:if="${pluginFinder.available('PluginSearchWidget')}"
    onclick="javascript: SearchWidget.open();"
    type="button"
  >
    搜索文章...
  </button>
</section>

<!-- 子分类宫格 -->
<section class="kb-grid-section">
  <th:block
    th:with="subCategories = ${categoryFinder.getByNames(category.spec.children)}"
  >
    <div class="kb-grid">
      <th:block th:each="subCat : ${subCategories}">
        <div
          th:with="subPosts = ${postFinder.listByCategory(1, 5, subCat.metadata.name)}"
          class="kb-card"
        >
          <!-- 卡片头部:图标 + 名称 + 文章数 -->
          <a th:href="@{${subCat.status.permalink}}" class="kb-card-header">
            <img
              th:unless="${#strings.isEmpty(subCat.spec.cover)}"
              th:src="${subCat.spec.cover}"
            />
            <h2 th:text="${subCat.spec.displayName}"></h2>
            <span th:text="|${subCat.postCount} 篇文章|"></span>
          </a>
          <!-- 文章预览列表 -->
          <ul class="kb-article-list">
            <li th:each="post : ${subPosts.items}">
              <a
                th:href="@{${post.status.permalink}}"
                th:text="${post.spec.title}"
              ></a>
            </li>
            <li th:if="${subPosts.total > 5}">
              <a
                th:href="@{${subCat.status.permalink}}"
                th:text="|查看全部 ${subPosts.total} 篇 →|"
              ></a>
            </li>
          </ul>
        </div>
      </th:block>
    </div>
  </th:block>
</section>

有两处值得注意:搜索按钮通过 pluginFinder.available('PluginSearchWidget') 判断是否安装了搜索插件,安装了才渲染,点击后调用 SearchWidget.open() 打开全站搜索弹窗;子分类的图标直接使用了 category.spec.cover,可以在后台为每个子分类上传一张图标图片。

第三步:子分类文章列表页

模板名称:category_knowledge_base.html

该模板使用分类模板的标准变量 category(当前分类信息)和 posts(分页文章列表),展示当前子分类下的所有文章。

面包屑导航

利用 categoryFinder.getBreadcrumbs(category.metadata.name) 自动生成完整路径,无需手动维护层级关系:

<nav
  th:with="breadcrumbs = ${categoryFinder.getBreadcrumbs(category.metadata.name)}"
>
  <ol>
    <li><a href="/">首页</a></li>
    <li th:each="bc, stats : ${breadcrumbs}">
      <a
        th:unless="${stats.last}"
        th:href="@{${bc.status.permalink}}"
        th:text="${bc.spec.displayName}"
      ></a>
      <span
        th:if="${stats.last}"
        th:text="${bc.spec.displayName}"
        aria-current="page"
      ></span>
    </li>
  </ol>
</nav>

文章列表

文章列表不显示封面,只展示标题和发布日期,保持知识库的专注感:

<ul class="post-list">
  <li th:each="post : ${posts.items}">
    <a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
    <time th:text="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
  </li>
</ul>

<nav th:if="${posts.hasPrevious() || posts.hasNext()}">
  <a
    th:href="@{${posts.prevUrl}}"
    th:classappend="${!posts.hasPrevious() ? 'disabled' : ''}"
    rel="prev"
    >
    上一页
  </a>
  <span th:text="|${posts.page} / ${posts.totalPages}|"></span>
  <a
    th:href="@{${posts.nextUrl}}"
    th:classappend="${!posts.hasNext() ? 'disabled' : ''}"
    rel="next"
    >
  下一页
  </a>
</nav>

第四步:知识库文章页

模板名称:post_knowledge_base.html

这一步并非必须。如果你希望知识库的文章和其他文章样式不同(比如去掉封面图、简化作者信息、侧边栏只保留目录),可以单独新建一个文章模板 post_knowledge_base.html

文章模板同样可以使用 categoryFinder.getBreadcrumbs 来生成面包屑,只需从文章的第一个分类开始反查路径:

<th:block
  th:unless="${#lists.isEmpty(post.categories)}"
  th:with="breadcrumbs = ${categoryFinder.getBreadcrumbs(post.categories[0].metadata.name)}"
>
  <!-- 面包屑结构同上 -->
</th:block>

在 Halo 后台配置

完成模板开发后,在 Halo 后台按以下步骤配置:

新建知识库根分类:自定义模板选择「知识库入口」,如果你为知识库的文章单独新建了文章模板,还可以在此处设置自定义文章模板为「知识库」。另外建议勾选在列表中隐藏,勾选之后该分类下的文章将不会出现在博客归档、首页文章列表等公共列表中,相当于把知识库内容从博客流中独立出来。

Halo 编辑文章分类,选择自定义模板

新建子分类:在根分类下新建若干子分类(如「入门指南」「常见问题」),自定义模板选择「知识库」。日后发布文章时只需勾选对应子分类即可自动归入知识库体系。

Halo 编辑文章分类,选择自定义模板

总结

整套知识库功能完全基于 Halo 现有机制实现,无需额外插件:

功能 机制
多套页面样式 theme.yaml 自定义分类模板
子分类列表 categoryFinder.getByNames(category.spec.children)
文章预览列表 postFinder.listByCategory(page,size,name)
面包屑导航 categoryFinder.getBreadcrumbs(name)
搜索入口 pluginFinder.available() + SearchWidget.open()
内容隔离 分类「在列表中隐藏」选项

这种「模板即功能」的思路在 Halo 主题开发中非常实用,同样可以用来做产品发布页、案例展示页等差异化内容区域。