目录

折腾 | 用数据文件优化短代码的使用

背景

网站上有一个阅览页面,用来记录我看过的书和电影。之前为了显示效果,自定义了一个叫 mediacard 的 Shortcode,可以在页面上生成卡片。

在 Markdown 文件里,每条记录都是一次 Shortcode 调用,像这样:

1
2
3
4
5
6
{{< mediacard title="大唐双龙传" 
    coverurl="..." link="..." 
    author="黄易" type="24-01" 
    starscore="★★★" greystar="☆☆" 
    intro="..." 
>}}

刚开始记录不多的时候,这个方法还行。但后来东西越来越多,整个 Markdown 文件就变得特别长,每次想加一条新记录,都要在文件里找很久,维护起来很麻烦。

解决方法

主要思路就是把数据页面分开。

Hugo 本身提供了 Data Files 功能,允许在 /data 目录下存放 YAML、JSON 或 TOML 格式的数据文件。这样,Markdown 文件就只负责页面结构和展示逻辑,而具体的书影条目数据,则统一放到数据文件里,方便管理。

具体步骤

1. 把数据转移到 data 文件夹

第一步是把之前写在 Markdown 文件里的所有条目数据,都整理出来,放到 /data 文件夹下,这种脏活累活当然是交给 AI 来完成啦!

我新建了 books.yamlmovies.yaml 两个文件。

修改前 (在 .md 文件里):

1
{{< mediacard title="法师故事" coverurl="..." author="索斯" type="24-08" ... >}}

修改后 (在 data/books.yaml 文件里):

1
2
3
4
5
6
7
8
- title: "法师故事"
  coverurl: "[https://bookcover.yuewen.com/qdbimg/349573/57821/600.webp](https://bookcover.yuewen.com/qdbimg/349573/57821/600.webp)"
  link: "[https://www.qidian.com/book/57821/](https://www.qidian.com/book/57821/)"
  author: "索斯"
  type: "24-08"
  starscore: "★★★★"
  greystar: "☆"
  intro: "魔兽践踏,巨龙咆哮,巫师诅咒,魔法璀璨之光照耀知识灯塔!"

这样一来,所有的数据都集中管理了,之后增删改查也只用编辑这两个 .yaml 文件就行。

2. 写一个新的 Shortcode

因为数据存放的位置变了,所以需要一个新的 Shortcode 来读取这些数据并显示出来。

我在 layouts/shortcodes/ 目录下新建了一个 medialist.html 文件。这个 Shortcode 接收两个参数:第一个是数据文件名(比如 “books”),第二个是年份(比如 “24”),用来筛选对应年份的数据。

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{{- $dataFile := .Get 0 -}}
{{- $yearPrefix := .Get 1 -}}

{{- if not $dataFile -}}
  {{- errorf "medialist shortcode in %s: 缺少第一个参数 (数据文件名)。" .Page.Path -}}
{{- end -}}
{{- if not $yearPrefix -}}
  {{- errorf "medialist shortcode in %s: 缺少第二个参数 (年份)。" .Page.Path -}}
{{- end -}}

{{- $mediaData := index site.Data $dataFile -}}

{{- if $mediaData -}}
  {{- $count := 0 -}}
  {{- range $mediaData -}}
    {{- if (hasPrefix .type $yearPrefix) -}}
      {{- $count = add $count 1 -}}
      <div class="media">
        <div class="media-cover" style="background-image:url(\{\{ .coverurl \}\})"></div>
        <div class="media-meta">
          <div class="media-meta-item title"><a href="\{\{ .link \}\}" target="_blank" rel="noopener noreferrer">\{\{ .title \}\}</a><span style="float:right;font-weight:400">\{\{ .type \}\}</span></div>
          <div class="media-meta-item">
            <span class="author">\{\{ .author \}\}</span>
            <span class="star-score">\{\{ .starscore \}\}<span class="grey-star">\{\{ .greystar \}\}</span></span>
          </div>
          <div class="media-meta-item intro">\{\{ .intro | safeHTML \}\}</div>
        </div>
      </div>
    {{- end -}}
  {{- end -}}

  {{- if eq $count 0 -}}
    <p style="text-align: center; color: #888;">本年度暂无记录。</p>
  {{- end -}}
{{- else -}}
  <p>警告:找不到数据文件 data/\{\{ $dataFile \}\}.yaml (或 .json/.toml)</p>
{{- end -}}

这个 shortcode 的工作原理:

  1. {{ $dataFile := .Get 0 }} 获取第一个参数("books")
  2. {{ $yearPrefix := .Get 1 }} 获取第二个参数("24")
  3. {{ range $mediaData }} 遍历数据文件中的所有条目
  4. {{ if (hasPrefix .type $yearPrefix) }} 是最关键的一行,它检查每个条目的 type 字段(如 "24-01")是否以我们传入的年份前缀("24")开头,只有匹配的条目才会被渲染成 HTML
  5. 还增加了一个计数器,如果某一年份下没有任何记录,会显示“本年度暂无记录”

3. 修改 Markdown 文件

最后一步,就是把原来的 Markdown 文件里大段的 mediacard 调用,换成新的 medialist

修改前 (2024年影剧部分):

1
2
3
4
5
{{< admonition type=tip title="影剧(20部)" open=false >}}
  {{< mediacard title="非诚勿扰3 (2023)" ... >}}
  {{< mediacard title="葬送的芙莉莲 (2023)" ... >}}
  ... (还有很多条)
{{< /admonition >}}

修改后:

1
2
3
{{< admonition type=tip title="影剧" open=false >}}
  {{< medialist "movies" "24" >}}
{{< /admonition >}}

原来几十行的内容,现在一行就够了,整个 Markdown 文件清爽了很多。

总结

这么修改之后,解决了之前维护困难的问题。

  • Markdown 文件变干净了,只负责页面结构,不再包含具体数据。
  • 数据管理方便,所有条目都在 .yaml 文件里,一目了然。
  • 之后扩展也容易,这些数据可以很方便地在网站其他地方复用。

总的来说,对 Hugo 这种需要管理大量结构化数据的情况,用 Data Files 功能是个不错的选择。

(2025-08-28@深圳)