☰
Hugo首页板块配置实战:用list模板打造模块化首页
2026/10/20 18:45:15 网站建设 项目流程

我实际用 Hugo 做首页也有小两年了,中间换过两套主题,最后干脆自己写。这系列前面聊了 baseof 结构和 partial 拆分,这篇集中把首页板块配置里最绕的 list 模板讲透。先给结论:Hugo 首页能不能按板块展示内容,核心不取决于首页模板本身,而取决于你对 list 模板及其数据上下文的掌握程度。

很多人一上手就给layouts/index.html写一堆循环,结果页面逻辑越堆越乱,改一个板块要动半天。真正顺手的做法是:把首页拆成若干板块,每个板块对应一段独立的数据筛选逻辑,这些逻辑统一交给 list 模板和 partial 去管,index.html 只做“拼装”。下面我按实际配置的先后顺序,一步一步拆开讲。

1. list 模板在 Hugo 中的角色定位

1.1 从模板查找顺序理解 list 的“管辖范围”

Hugo 的模板系统是基于查找顺序工作的。一个页面该用哪个模板渲染,不是你在 front matter 里手动指定,而是 Hugo 根据页面类型自动匹配。首页(Homepage)对应的模板是/layouts/index.html,而 list 模板是个更大的家族,它负责所有“列表性质”的页面。

具体来说,list 模板覆盖四类页面:

  • 首页(homepage),如果 index.html 不存在,Hugo 会回退到_default/list.html
  • 区块页(section),比如/content/posts/对应的列表页
  • 分类页(taxonomy),比如/categories/tech/这类归档
  • 分类词页(term),比如某个具体标签下的列表

这里有个关键认知:首页虽然用 index.html 渲染,但它也是一个 list 页面,因为它本质上要展示一组页面的集合。这就是为什么在 index.html 里你依然可以使用.Pages、.Site.RegularPages这类 list 数据上下文。理解这一点,你就能明白为什么首页板块配置离不开 list 模板——你实际上是在首页模板里嵌套调用 list 的逻辑。

1.2 list 模板的三层数据来源

list 模板要展示数据,必须要知道“数据从哪来”。Hugo 给你提供了三个层次的数据上下文,我简单梳理一下:

数据对象作用域典型用途
.Pages当前页面的直接子页面在 section 页里非常常用,比如 posts 区块下的所有文章
.Site.RegularPages全站所有常规文章页手动筛选全站文章分类,适合首页板块
.Site.Taxonomies全站分类和标签聚合按分类自动归档,生成分类列表头部和文章集合

我之前一直用.Pages去取首页下的子页面,后来发现不对劲——首页的子页面就是各个 section,而不是文章本身。你要拿文章列表,必须在首页场景下用.Site.RegularPages做筛选,或者用.Site.Taxonomies从分类角度去聚合。这是首页板块配置里第一个容易栽的坑。

1.3 首页板块为什么要“借用” list 模板

直说:如果你只做单栏目博客,首页放一个最新文章列表就够了,不需要板块。但只要你的博客有分类、有标签、有不同类型的文章,首页就必须按板块展示,否则访客进来看到一锅乱炖,根本不知道你站点的内容结构是什么。

用 list 模板做板块,最大的优势是筛选逻辑的复用。假设你要在首页展示“技术笔记”分类下的最新 5 篇,同时展示“随笔”分类下的最新 3 篇,这两个板块共用一个卡片样式,只是数据源不同。如果你把列表渲染逻辑写进 index.html 里,就得复制两遍;如果你把它抽成一个 partial,再结合 list 模板的数据筛选,就可以只写一遍,传不同的参数进去。

我在实际项目中,把首页拆成几个板块,但它们的渲染样式统一调用一个叫post-card.html的 partial,数据筛选则通过不同的 list 模板变量传入。改布局只动一个文件,改数据只要调参数,维护成本立刻降下来。

2. 首页板块配置的整体设计思路

2.1 板块拆解的思考方式:先定功能,再定数据

做首页板块配置前,我建议你先画一张“板块地图”,把首页要展示的内容模块列出来。不要上来就写代码,先问自己几个问题:

  • 首页要展示哪些板块?(比如:置顶推荐、最新文章、分类精选、标签云)
  • 每个板块的数据来源是什么?(是全站筛选,还是某个分类?是手动指定,还是按时间排序?)
  • 每个板块展示几条内容?(3 条、5 条还是 10 条?)
  • 板块之间是否需要排除重叠?(比如最新文章板块和分类精选板块很可能出现同一篇文章)

这张表看起来简单,但它直接决定了你的模板结构和变量设计。我见过很多项目,首页越改越乱,就是因为一开始没有明确这些边界,导致板块之间数据互相覆盖,改一个板块影响另一个。

2.2 三种常见的首页板块数据方案对比

根据数据来源的不同,我总结出三种主流方案:

  1. 按 section 分区:把文章按内容目录(如content/tech/、content/life/)划分,首页板块直接渲染每个 section 的最新文章。优点是结构清晰,目录和板块一一对应;缺点是如果一篇文章要出现在多个板块,比如技术文章同时归到“教程”和“踩坑”两个板块,这种方案就比较别扭。

  2. 按分类(taxonomy)聚合:用categories或tags作为板块划分依据,通过.Site.Taxonomies来聚合文章。优点是灵活,一篇文章可以有多个分类,可以出现在多个板块;缺点是筛选逻辑要写得仔细,尤其要注意分类名和板块名的映射。

  3. 按参数手动指定:给每篇文章的 front matter 加一个featured = true或section_weight参数,再在首页用where筛选。优点是可以完全控制每篇文章出现在哪个位置,适合做编辑推荐;缺点是文章一多,手动维护 front matter 就成了负担。

我在实际项目中,首页板块通常是方案二加方案三的混合:大板块按分类聚合,小板块(如“编辑推荐”)手动指定参数筛选。这样既保持了灵活性,又能保证一些关键位置的内容质量可控。

2.3 板块数据流的核心链路:index → partial → list

首页板块配置的代码组织,我建议遵循一条清晰的数据流链路:

index.html(页面骨架) → 板块容器(section, 每个板块一个容器) → 数据筛选(list 模板逻辑,where / intersect / limit) → 渲染列表(post-card 等 partial,接收单篇文章上下文)

index.html 只负责定义页面结构,不直接写列表渲染逻辑。每个板块的数据筛选交给一个专门的 partial,或者直接写在一个range前的变量定义里。渲染单篇文章卡片的逻辑抽成一个通用 partial,通过.传入当前文章上下文。

这条链路的好处是:当你需要调整某个板块的展示数量时,你只需要改板块 partial 里的limit参数,不需要动 index.html;当你需要调整卡片样式时,只需要改 post-card partial,所有板块的卡片样式同步更新。

2.4 板块配置里必须规避的三个“伪需求”

这里分享三个我刚开始做板块时频繁踩坑的点,这几个问题本质上不是语法问题,而是需求理解的问题:

第一,板块并不是越多越好。首页板块太多会让页面变得臃肿,访客滑动半天看不到重点信息。建议核心板块不超过 5 个,每个板块默认展示 3~5 条内容。

第二,“最新文章”板块并不需要和“分类精选”完全隔离。很多同学遇到重复内容就拼命去重,其实首页板块之间内容轻微重叠是可以接受的,访客并不会因为一篇文章出现在了推荐位又出现在分类列表里就觉得站点有问题。过度去重反而容易让筛选逻辑变得复杂、代码啰嗦,得不偿失。

第三,不要把板块配置写死在模板里。我前面提到,板块标题、显示数量、是否展示摘要等信息,都应该通过 partial 的参数传入。这样后续如果你想做后台可配置的首页,或者针对不同页面复用板块样式,都不需要重构模板。

3. list 模板核心语法与数据筛选实操

3.1 基础变量声明:创建首页板块的数据集合

Hugo 模板是 Go 语言的 template 包实现的,你可以在模板开头用{{- $变量名 := 表达式 -}}声明变量。在首页板块配置里,我通常在每个板块前面先定义好数据源,再进行遍历渲染。比如:

{{- $techPages := where .Site.RegularPages "Params.categories" "tech" -}} {{- $techPosts := first 5 $techPages -}}

这两行的含义是:先从全站的常规文章中,筛选出categories参数为tech的文章,再取出其中的前 5 篇。这个写法在首页板块配置里非常常用,第一行的where函数负责筛选,第二行的first函数负责截取数量。

3.2 where 筛选的常见坑:参数层级与数据类型匹配

where是 Hugo 板块配置的筛选利器,但新手很容易踩一个坑:参数层级搞错。比如我的 front matter 是这样写的:

--- title: "我的文章" categories: - tech - life tags: - hugo - web ---

注意,categories和tags在 Hugo 里的默认数据类型是数组,不是字符串。如果你用where .Site.RegularPages "Params.categories" "tech"去筛选,得到的结果很可能为空,因为 Hugo 不会自动将一个数组参数和单个字符串做相等匹配。

正确做法有两个:一是用intersect做交集判断,比如“包含 tech 分类的文章”;二是直接遍历分类聚合。我推荐的做法是,如果要做板块配置,优先用.Site.Taxonomies获取分类下的文章列表,避免手动处理数据类型不匹配的问题。

3.3 组合筛选:用 intersect 与 complement 控制板块内容

当板块配置复杂起来后,单一的条件筛选就不够用了。我举两个实际场景。

场景一:首页要展示同时属于“教程”和“Hugo”两篇文章,也就是两个分类交集。代码如下:

{{- $tutorialPages := where .Site.RegularPages "Params.categories" "tutorial" -}} {{- $hugoPages := where .Site.RegularPages "Params.categories" "hugo" -}} {{- $intersectPages := intersect $tutorialPages $hugoPages -}} {{- range first 5 $intersectPages -}} {{- .Title -}} {{- end -}}

场景二:首页“最新文章”板块要排除掉“置顶推荐”板块已经展示过的文章,这时就可以用complement。假设置顶推荐文章的 front matter 里有一个featured = true的参数:

{{- $regularPages := .Site.RegularPages -}} {{- $featuredPages := where .Site.RegularPages "Params.featured" true -}} {{- $otherPages := complement $featuredPages $regularPages -}} {{- range first 5 $otherPages -}} {{- .Title -}} {{- end -}}

3.4 排序控制:weight、date 与自定义参数

Hugo 的默认排序是按weight从小到大,再按日期从新到旧。如果你想按自己的规则排序,可以在range前加排序函数。我首页板块经常用的排序有两种:

第一种,按日期倒序,展示最新文章:

{{- $latestPages := first 5 ( .Site.RegularPages.ByDate.Reverse ) -}}

第二种,按自定义参数排序,比如按置顶权重:

{{- $featuredPages := sort (where .Site.RegularPages "Params.featured" true) "Params.weight" "asc" -}}

这里有个细节要提醒你:sort函数对数组参数的排序,是按照参数的字符串值或数字值来排的,不是按自然语义。如果你希望weight = 2排在weight = 10前面,那么请用数字类型,不要用字符串类型。在 front matter 里写weight: 2和weight: "2"是截然不同的。

3.5 空数据时的优雅降级

首页板块配置还有一个很重要的环节:板块数据为空时的处理。比如某个分类下暂时没有文章,直接range会输出一个空列表,页面看起来像一块排版空缺。我的习惯是在每个板块 partial 里加一个判断:

{{- $techPosts := first 5 (where .Site.RegularPages "Params.categories" "tech") -}} {{- if $techPosts -}} <div class="section-block"> <h2>技术笔记</h2> <div class="post-list"> {{- range $techPosts -}} {{- partial "post-card.html" . -}} {{- end -}} </div> </div> {{- end -}}

这样当分类下还没有文章时,这个板块整体不渲染,页面布局不会出现“标题在、内容空”的尴尬情况。这个习惯是我做了几个站点后总结出来的,很多人的首页出现一块空白,就是没有做这个空值判断。

4. 首页板块布局的完整实现方案

4.1 目录结构设计与模板文件配置

我建议你把首页板块相关文件组织成这样的结构:

layouts/ ├── index.html ├── _default/ │ ├── baseof.html │ ├── list.html │ └── single.html └── partials/ ├── head.html ├── header.html ├── footer.html ├── home/ │ ├── hero.html │ ├── latest.html │ ├── featured.html │ └── categories.html └── post-card.html

index.html 只负责把home/目录下的板块 partial 按顺序拼装起来。latest.html、featured.html这些 partial 内部,则使用 list 模板逻辑去筛选数据、调用 post-card 渲染卡片。这样的结构,我维护下来是最清晰的。

4.2 index.html 的骨架:板块拼装

一个典型的layouts/index.html长这样:

{{- define "main" -}} <main class="home-page"> {{- partial "home/hero.html" . -}} {{- partial "home/featured.html" . -}} {{- partial "home/latest.html" . -}} {{- partial "home/categories.html" . -}} </main> {{- end -}}

注意:这个文件里没有直接写任何range、where的代码。所有数据筛选和渲染逻辑都在对应 partial 里。这样做的最大好处是:你想调整首页板块顺序,只要调整 partial 的调用顺序;你想暂时隐藏某个板块,注释掉一行就行;你想增加一个板块,新增一个 partial 再在 index.html 里加一行调用。

4.3 板块 partial 的三种典型案例

案例一:最新文章板块(所有文章按日期倒序取前 5 条)

<!-- layouts/partials/home/latest.html --> {{- $latestPosts := first 5 ( .Site.RegularPages.ByDate.Reverse ) -}} {{- if $latestPosts -}} <section class="home-section home-section-latest"> <div class="section-header"> <h2>最新文章</h2> <a href="/posts/">查看全部</a> </div> <div class="post-grid"> {{- range $latestPosts -}} {{- partial "post-card.html" . -}} {{- end -}} </div> </section> {{- end -}}

案例二:按分类选择性展示板块(这里只展示“技术”和“随笔”两个板块)

<!-- layouts/partials/home/categories.html --> {{- $categories := slice "tech" "life" -}} {{- range $categories -}} {{- $category := . -}} {{- $posts := first 3 (where $.Site.RegularPages "Params.categories" $category) -}} {{- if $posts -}} <section class="home-section home-section-category"> <div class="section-header"> <h2>{{ $category }}</h2> <a href="/categories/{{ $category }}/">更多</a> </div> <div class="post-grid"> {{- range $posts -}} {{- partial "post-card.html" . -}} {{- end -}} </div> </section> {{- end -}} {{- end -}}

案例三:手动推荐板块(通过 front matter 的featured参数筛选)

<!-- layouts/partials/home/featured.html --> {{- $featuredPosts := first 3 (sort (where .Site.RegularPages "Params.featured" true) "Params.featured_weight" "asc") -}} {{- if $featuredPosts -}} <section class="home-section home-section-featured"> <div class="section-header"> <h2>编辑推荐</h2> </div> <div class="post-grid"> {{- range $featuredPosts -}} {{- partial "post-card.html" . -}} {{- end -}} </div> </section> {{- end -}}

4.4 板块参数化:用 partial 传参实现复用

前面几个案例的 partial 是写死的,如果仅用于首页,这样没问题。但如果你有多个页面需要复用同一个“文章列表板块”,我建议把板块 partial 参数化。比如我把分类板块 partial 改成:

<!-- layouts/partials/home/category-section.html --> {{- $category := .category -}} {{- $limit := .limit | default 3 -}} {{- $title := .title | default $category -}} {{- $posts := first $limit (where $.Site.RegularPages "Params.categories" $category) -}} {{- if $posts -}} <section class="home-section"> <div class="section-header"> <h2>{{ $title }}</h2> <a href="/categories/{{ $category }}/">更多</a> </div> <div class="post-grid"> {{- range $posts -}} {{- partial "post-card.html" . -}} {{- end -}} </div> </section> {{- end -}}

然后在 index.html 里这样调用:

{{- partial "home/category-section.html" (dict "category" "tech" "limit" 5 "title" "技术干货") -}}

这里我用了dict函数构造了一个字典作为 partial 的上下文,partial 内部通过.category、.limit、.title来读取参数。这种做法的好处非常明显:你只需要维护一个板块 partial,无论首页有多少个分类板块,都通过传参生成。要调整某个板块的展示数量,改一行调用即可。

4.5 卡片 partial 的设计:板块里最重要的渲染单元

板块列表最终会落到一张张文章卡片上。我强烈建议你单独建一个post-card.htmlpartial,这是首页板块配置里的最低层渲染单元。它接收单篇文章的上下文,用来输出文章标题、日期、摘要、分类等信息。我的 post-card partial 大致长这样:

<!-- layouts/partials/post-card.html --> <article class="post-card"> <h3 class="post-card-title"> <a href="{{ .RelPermalink }}">{{ .Title }}</a> </h3> {{- with .Params.summary -}} <p class="post-card-summary">{{ . }}</p> {{- else -}} <p class="post-card-summary">{{ .Summary | truncate 80 }}</p> {{- end -}} <div class="post-card-meta"> <time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "2006-01-02" }}</time> {{- with .Params.categories -}} <span class="post-card-categories"> {{- range . -}} <a href="/categories/{{ . | urlize }}/">{{ . }}</a> {{- end -}} </span> {{- end -}} </div> </article>

这个 partial 的细节值得说道说道。.Summary如果不想用,可以用.Params.summary从 front matter 里取自定义摘要,取不到再回退到.Summary,这样文章卡的展示效果更可控。.RelPermalink是相对链接,比起硬编码域名,部署到子目录时能省很多事。

5. 板块排序、排除与显示数量的综合控制

5.1 手动控制板块顺序:建议不要依赖 Hugo 默认规则

首页板块的顺序,很多人会直接在 index.html 里按顺序写死 partial 调用,这个是最简单也最稳定的做法。如果你希望板块顺序可以配置化,比如在某些页面里的板块顺序不同,那么就需要把“板块列表”定义成一个数组,在模板里遍历。这个相对复杂,我一般只在做多站点或者需要视觉自定义的项目里才用。对绝大多数博客场景,在 index.html 里把 partial 调用顺序写死就够了,因为首页板块的顺序本质上是一个站点的设计决策,不会频繁改变。

5.2 排除板块内重复文章:首推 complement 或自定义参数

之前提到过,板块之间内容重叠在大多数情况下可以接受。但如果你确实需要去重,我推荐两种做法。

做法一:在数据筛选阶段,用complement把前面板块已经用到的文章排除掉。这个方案需要你先定义一个“已用文章”的集合,然后在后续板块的筛选里,用complement取出差集。这种做法适合板块相对固定的场景,代码逻辑稍复杂,但筛选结果比较干净。

做法二:在 front matter 层面手动控制。比如给不需要出现在首页的文章加一个exclude_home = true参数,然后在所有首页板块的数据筛选中都加上where "Params.exclude_home" "!=" true。这种做法最直接,适合你想精确控制哪些文章不出现在首页的场景。

5.3 显示数量与分页:首页板块一般用 first 而不是 paginate

首页板块配置里,每个板块默认展示 5 篇以内是合理的。如果需要“查看更多”,直接链接到对应分类页面即可,比如上一节案例里的“查看全部”链接。首页本身一般不做分页,分页留给_default/list.html和分类列表页去处理。

如果你想在首页做“加载更多”或“滚动加载”,复杂度和方案选型会显著上升。我个人的建议是:博客首页不做加载更多,因为访客更关心的是快速浏览内容结构,而不是在一个列表里无限翻页。

5.4 多语言站点的板块数据注意点

如果你的站点启用了多语言(i18n),那么.Site.RegularPages会包含所有语言的文章。做首页板块配置时,你需要按.Lang筛选当前语言的内容。Hugo 对多语言的支持是内置的,通常你只需要在数据筛选中加上:

{{- $pages := where .Site.RegularPages "Lang" .Language.Lang -}}

不加这句,你可能会在中文首页看到英文文章列表,或者反过来。这是一个很容易被忽视但在多语言站点里特别必要的细节。

6. 常见问题与排查技巧实录

6.1 板块不显示任何内容:先查 front matter 里的分类写法

我排查过很多次首页板块不显示的问题,最后发现 80% 的根源在 front matter 的分类数据类型上。前面我提到过,如果你的 front matter 是这样写的:

categories: tech

这个写法在 Hugo 里会被解析成字符串"tech",而不是数组["tech"]。而 Hugo 的分类输出是一个数组,你用where "Params.categories" "tech"筛选时,很可能匹配不到。更好的写法是:

categories: - tech - hugo

两者在页面展示时差异不大,但在.Params.categories的数据类型上差异巨大,直接影响where筛选的结果。遇到板块突然不显示,第一反应先看 front matter 的数据类型。

6.2 板块显示顺序和预期不一致:检查 weight 和日期

Hugo 列表页默认按weight升序、日期降序排序。如果你在筛选后使用了first 5,取出的是经过默认排序后的前 5 条。如果你的文章 front matter 里设置了weight,那么“最新文章”板块理论上应该按日期倒序,但实际却可能受到weight的影响,导致顺序不对。

解决办法是显式调用排序函数,不要依赖默认排序。比如最新文章板块用.ByDate.Reverse,推荐板块用自定义参数的sort。

6.3 板块内容重复,无法通过简单 where 排除

前面我提到了complement方案。实际使用时,要根据板块数据源的不同灵活调整。比如“推荐”板块的数据来自.Site.RegularPages加where,而“最新”板块的数据也来自.Site.RegularPages,要排除掉已推荐文章,直接在最新板块的筛选逻辑里做一个差集就行。

我还在项目里用过另一种更粗暴但有效的办法:给文章加exclude_from_home = true参数,写入 front matter,然后在所有首页板块的筛选中都加上排除条件。这个方法虽然要每个板块都写一遍,但胜在简单、直观,团队协作时不容易出错。

6.4 常见报错与报错日志速查表

现象可能原因解决思路
页面渲染空白,控制台报 “execute of template failed”模板语法错误,通常是变量名拼错或函数参数类型不匹配优先看 Hugo 输出日志里的文件名和行号,定位到对应 partial 或模板文件
首页只显示标题没有内容数据筛选结果为空先在_default/list.html里输出.Site.RegularPages数量,排查数据源是否为空
分类标签不显示最后某个模板里.Params.tags不是数组给文章 front matter 使用数组格式的分类标签,并在渲染前用range遍历
分页无效或页码不跳转首页或 list 模板没有配置Paginate相关参数确认config.toml中paginate项是否设置,并在模板中使用.Paginator而不是手写分页
文章链接指向 404.RelPermalink或.Permalink使用不当确保前端使用了.RelPermalink,避免硬编码地址

6.5 排查工具与技巧:利用 Hugo 生成器自带的调试输出

Hugo 有个好用但容易被人忽略的功能——hugo --debug和hugo server --verbose。前者能输出详细的模板执行过程,后者能实时预览并输出渲染错误。我在排查首页板块问题时,经常用的一个技巧是,在模板开头临时输出一些变量值:

{{- printf "调试输出:共 %d 篇文章" (len .Site.RegularPages) | safeHTML -}}

或者把某个集合的部分信息直接输出到页面:

{{- range first 3 .Site.RegularPages -}} {{- .Title }} | {{ .Params.categories -}}<br> {{- end -}}

这些临时输出在排查数据源问题时非常高效,定位完就能删掉。比起猜测哪个环节出了问题,直接看输出最可靠。

7. 实际项目中的首页板块配置经验总结

7.1 组件化是唯一值得走的路

说到这,我想把最重要的经验放在最后面:首页板块配置,务必走组件化路线。index.html 只留结构,每个板块独立成 partial,数据筛选与渲染样式分离。这样不管你是用一个分类板块 partial 传参打天下,还是每个板块都写独立 partial,至少你改动的时候知道自己要动哪个文件。

我见过太多人把首页所有逻辑写进一个巨大的 index.html,改一个板块要上下翻半天,最后自己也绕晕了。组件化虽然初期要花一点时间设计,但后期维护成本会大幅下降。

7.2 先定数据结构,再写模板

这一点是经验之谈。很多项目越到后期越难改,根源在于文章 front matter 的数据结构没定好。比如分类的类型、是否启用摘要字段、推荐权重如何命名,这些字段在设计之初就要定下来。否则你后续想在首页加一个新板块,发现没有对应的数据可用,或者数据结构不统一,筛选逻辑写得异常痛苦。

我目前约定的一套常用的 front matter 规范是这样的:

--- title: "首页板块配置实战" date: 2025-01-15 description: "本文介绍 Hugo 首页板块配置与 list 模板的结合使用" categories: - blog tags: - hugo - ubuntu featured: false featured_weight: 0 hidden_from_home: false ---

这套字段不仅能支撑首页板块配置,还能支撑文章页的相关推荐、置顶推荐等功能。所以我的建议是:花点时间把 front matter 规范定好,再开始写模板,事半功倍。

7.3 善用 Hugo 内置函数和列表处理的组合

Hugo 模板内置了大量处理列表数据的方法,把这些方法组合起来,就可以覆盖首页板块的各种需求。我列一下我个人最常用的几个组合:

<!-- 按分类展示前 N 篇 --> {{- $pages := (where .Site.RegularPages "Params.categories" "hugo") -}} {{- $posts := first 5 $pages -}} <!-- 按自定义参数排除某些文章 --> {{- $pages := (where .Site.RegularPages "Params.hidden_from_home" "!=" true) -}} <!-- 按自定义参数排序 --> {{- $pages := sort (where .Site.RegularPages "Params.featured" true) "Params.featured_weight" "asc" -}} <!-- 多条件筛选:分类 + 排除 --> {{- $base := (where .Site.RegularPages "Params.hidden_from_home" "!=" true) -}} {{- $pages := (where $base "Params.categories" "hugo") -}}

不要把.Site.RegularPages一直重复使用,最好先把它赋值给一个变量,然后基于这个变量做链式筛选。这样既减少模板解析的时间,也让代码更整洁。

7.4 最后分享一个实用小技巧:利用.Scratch在板块间传递数据

如果你的首页板块逻辑比较复杂,比如一个板块的分类列表要依赖另一个板块的数据,.Scratch可以用来在模板之间存储和读取临时数据。这个功能我第一次用的时候觉得多余,后来做复杂首页的时候才发现它是神器。

举个例子,假设你要在首页收集所有被板块引用的文章 ID,然后传递给页面底部一个“你可能感兴趣”的模块,用来排除已经展示的文章。你可以这样:

{{- if not (.Scratch.Get "homeUsedPosts") -}} {{- .Scratch.Set "homeUsedPosts" (slice) -}} {{- end -}}

然后在每个板块里把使用的文章 ID 加进这个 slice,最后在底部模块里剔除这些文章。这个技巧在大型首页板块配置里非常实用,能让你在多个板块之间共享数据,而不用设计复杂的全局变量。

8. 从首页板块到全站模板的扩展思路

8.1 将板块 partial 复用到分类页和其他列表页

首页板块配置中沉淀下来的 partial 和筛选逻辑,完全可以复用到其他列表页。比如我在首页用category-section.html这个 partial 渲染分类板块,在分类页里,我只需要把数据源换成当前分类的文章集合,就可以复用同样的排版和卡片样式。

这就是组件化的最大价值——同一个 partial,通过传入不同的数据环境,可以在全站范围内使用。你不必为分类页重新发明一套轮子,也不用担心首页和分类页的样式不一致。

8.2 从 list 模板到 taxonomy 模板的迁移路径

在你熟悉了首页的板块配置后,建议再深入了解 taxonomy 模板(如分类页、标签页)的定制。两者的数据上下文略有区别,但渲染逻辑基本上是相通的。你在首页板块里学会的where、intersect、complement、sort、first等用法,在分类页里几乎可以原样使用。

我之前把首页的“分类精选”板块样式迁移到分类页时,改动量非常小,就是换了一下数据源,然后微调了标题部分而已。

8.3 何时需要放弃首页板块的“自定义”,回到默认列表

每个项目都有它的特殊之处,首页板块配置也不是所有博客都必须做的。如果你的站点内容少、分类简单,用默认的 list 模板渲染一个最新的文章列表就足够了,不需要做复杂板块。

我个人的判断标准是:当你的文章数量不超过 50 篇,或者只有单一主题时,首页板块的意义不大。过多的板块反而让读者找不到重点。做一个干净利落的最新文章列表,有时候比炫酷的板块布局更实用。

我在实际项目中见过好几个反面案例,一开始把首页板块配置得很复杂,后来文章量上来了,发现维护成本居高不下,最后又回到简化路线。所以,板块配置一定要结合自己的内容情况量力而行。

8.4 我踩过一次印象很深的坑:模板缓存导致板块不更新

这个必须拿出来单独说一下。Hugo 有一套内容与模板缓存机制,如果你使用hugo server开发环境,通常文件变更会自动刷新;但如果你在站点目录下改乱了某些缓存配置,或者使用了一些持续集成工具,就很容易出现“模板改了但页面没变”的情况。

排查方法很简单:先强制刷新浏览器缓存,然后停掉hugo server,重新运行一次。如果还是不行,执行hugo --gc清一次缓存。这个参数的作用是运行一次垃圾回收,清理掉不需要的缓存文件。很多看似莫名其妙的问题,清一次缓存就消失了。

说到底,首页板块配置这件事,技术难度真的不高,核心是要把思路理清楚,把数据结构定好,把组件拆到位。你把这些基础打牢,后面无论怎么调整板块、增加模块,都是顺手的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询