最美的陷阱

Hugo 模板是基于 Go 的 html/template 引擎。初看非常简洁——{{ .Title }}{{ range .Pages }}——但一旦深入,你会发现一个充满陷阱的世界。

这篇文章记录我在开发幻梦过程中被 Go Template 整蛊的瞬间。

上下文(Context)的消失

这是最常见的坑:

{{ with .Params }}
  <!-- 现在 "." 是 .Params,不再是 Page -->
  {{ .Title }}  <!-- 这访问的是 .Params.Title,不是 Page.Title!-->
{{ end }}

withrange 会改变上下文。解决方案是提前保存引用:

{{ $page := . }}
{{ range .Pages }}
  {{ $page.Title }}  <!-- 一直指向 Page -->
{{ end }}

Partial 参数传递

{{ partial "card.html" . }}

<!-- card.html 内部 -->
<h3>{{ .Title }}</h3>  <!-- "." 是被传入的上下文 -->

复杂场景需要传递多个参数,用 dict

{{ partial "card.html" (dict "Page" . "Style" "grid" "Index" $index) }}

<!-- card.html 内部 -->
{{ $page := .Page }}
{{ $style := .Style }}

dict 创建一个包含键值的 map 作为上下文。这是 Hugo 的 Swiss Army Knife。

Scratch:共享变量

不同 partial 之间不能直接共享变量。Scratch 是 Hugo 的全局便签:

{{ .Scratch.Set "counter" 0 }}
{{ .Scratch.Add "counter" 1 }}
{{ .Scratch.Get "counter" }}

在幻梦中用于跨组件共享状态(如判断文章是否有更新标记)。

字符串拼接

Go Template 没有 + 运算符用于字符串。要用 printf

{{ $url := printf "%s/%s" $base $slug }}

日期格式

Go 的日期格式化用魔法时间 Mon Jan 2 15:04:05 MST 2006

{{ .Date.Format "2006-01-02" }}           <!-- 2026-04-24 -->
{{ .Date.Format "2006年01月02日" }}        <!-- 2026年04月24日 -->
{{ .Date.Format "January 2, 2006" }}       <!-- April 24, 2026 -->

JSON 输出

jsonify 函数将 Go 数据结构转换为 JSON:

<script>
  window.i18nData = {{ dict "all" $translations | jsonify }};
</script>

这在幻梦中用于生成搜索索引和注入 i18n 数据到 JS。

最令人困惑的特性

Go Template 没有 else if。你要嵌套:

{{ if eq $a 1 }}
  A
{{ else }}
  {{ if eq $a 2 }}
    B
  {{ else }}
    C
  {{ end }}
{{ end }}

Go Template 像一个严格的语文老师。语法对了就很快乐,错了一个标点就报错并且告诉你"undefined"。