Skip to main content

ZSL

20260705 關注點分離

Published:
Updated:

這其實不是關注點分離而是關注點搶光光。寫這篇文章的原因是 hugo blowfish 我改 A 壞 B,但是我真的要找一下藉口:這個 A 語法在 10 個地方都重複,我每次要修 A 我就要在 10 個地方一一檢查調用是否正確,而不是在同一個地方把精力花在想好 A 語法正不正確...因為我的關注點被 10 次重複使用搶走了,同樣的精力消耗我有 90% 都在重複檢查調用而不是語法本身,尤其 go-template 又會因為一個大小寫、一個 dollar sign 導致 scope 完全錯誤。

那當初我都已經做那麼多 refactor 了,怎麼不合併這 10 個調用呢?因為原版的代碼更慘,慘到沒有意識到、也沒精力去合併他,這也算是一種另類的關注點搶光光。

原版

{{ $disableImageOptimization := .Page.Site.Params.disableImageOptimization | default false }}
{{ with .Params.externalUrl }}
<a href="{{ . }}" target="_blank" rel="external" class="min-w-full">
  {{ else }}
  <a href="{{ .RelPermalink }}" class="min-w-full">
    {{ end }}
    <div class="min-h-full border border-neutral-200 dark:border-neutral-700 border-2 rounded overflow-hidden shadow-2xl relative">

      {{- with $.Params.images -}}
      {{- range first 6 . }}
      <meta property="og:image" content="{{ . | absURL }}" />{{ end -}}
      {{- else -}}
      {{- $images := $.Resources.ByType "image" -}}
      {{- $featured := $images.GetMatch "*feature*" -}}
      {{- if not $featured }}{{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }}{{ end -}}
      {{ if and .Params.featureimage (not $featured) }}
      {{- $url:= .Params.featureimage -}}
      {{ $featured = resources.GetRemote $url }}
      {{ end }}
      {{- if not $featured }}{{ with .Site.Params.defaultFeaturedImage }}{{ $featured = resources.Get . }}{{ end }}{{ end -}}
      {{ if .Params.hideFeatureImage }}{{ $featured = false }}{{ end }}
      {{- with $featured -}}
      {{ if or $disableImageOptimization (strings.HasSuffix $featured ".svg")}}
        {{ with . }}
        <div class="w-full thumbnail_card nozoom" style="background-image:url({{ .RelPermalink }});"></div>
        {{ end }}
      {{ else }}
        {{ with .Resize "600x" }}
        <div class="w-full thumbnail_card nozoom" style="background-image:url({{ .RelPermalink }});"></div>
        {{ end }}
      {{ end }}
      {{- else -}}
      {{- with $.Site.Params.images }}
      <meta property="og:image" content="{{ index . 0 | absURL }}" />{{ end -}}
      {{- end -}}
      {{- end -}}

      {{ if and .Draft .Site.Params.article.showDraftLabel }}
      <span class="absolute top-0 right-0 m-2">
        {{ partial "badge.html" (i18n "article.draft" | emojify) }}
      </span>
      {{ end }}

      {{/* skip */}}
      </div>
    </div>
  </a>
  1. 毫無排版極度難以閱讀,有些 if-else 換行,有些不換行,沒有 indent 連續 30 行的 if-end/with-end/range-end 根本找不到對應關係
  2. {{- -}}{{ }} 混用且無規律
  3. Go template 資料處理邏輯和 HTML 外觀結構耦合
  4. 錯誤不合規的 og:image
  5. 型別混用
  6. 處理邏輯混亂,不尊重 front matter 設定,反而先看檔案名稱
  7. featureimage 不支援內部連結

不只這樣,這種東西在 10 個地方出現,而我要看 10 次理解每個地方是不是有不同、有不同的是故意的還是寫錯的、go template 語法有大小寫、dollar sign 一個字意思完全不一樣,混亂程度直線倍增。

我 refactor 後的新版

{{/* Used by
  1. list.html and term.html (when the cardView option is enabled)
  2. Recent articles template (when the cardView option is enabled)
  3. Shortcode list.html
*/}}
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}

{{ $page := .Page }}
{{ $featured := "" }}
{{ $featuredURL := "" }}
{{ $thumbnailAspectRatio := site.Params.thumbnailAspectRatio | default "1.5" }}
{{ $thumbnailAspectRatio = replaceRE "[^0-9.]" "" $thumbnailAspectRatio }}
{{ if eq $thumbnailAspectRatio "" }}{{ $thumbnailAspectRatio = "1.5" }}{{ end }}
{{ if not .Params.hideFeatureImage }}
  {{/* frontmatter */}}
  {{ with $page }}
    {{ $p := . }}
    {{ with .Params.featureimage }}
      {{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
        {{ if site.Params.hotlinkFeatureImage }}
          {{ $featuredURL = . }}
        {{ else }}
          {{ $featured = resources.GetRemote . }}
        {{ end }}
      {{ else }}
        {{ $featured = $p.Resources.Get . }}
      {{ end }}
    {{ end }}

    {{/* page resources */}}
    {{ if not (or $featured $featuredURL) }}
      {{ $images := .Resources.ByType "image" }}
      {{ range slice "*feature*" "*cover*" "*thumbnail*" }}
        {{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
      {{ end }}
    {{ end }}

    {{/* fallback to default */}}
    {{ if not (or $featured $featuredURL) }}
      {{ $default := site.Store.Get "defaultFeaturedImage" }}
      {{ if $default.url }}
        {{ $featuredURL = $default.url }}
      {{ else if $default.obj }}
        {{ $featured = $default.obj }}
      {{ end }}
    {{ end }}

    {{/* generate image URL if not hotlink */}}
    {{ if not $featuredURL }}
      {{ with $featured }}
        {{ $featuredURL = .RelPermalink }}
        {{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
          {{ $featuredURL = (.Resize "600x").RelPermalink }}
        {{ end }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}


<article
  class="article-link--card relative min-h-full min-w-full overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600">
  {{ with $featuredURL }}
    <div class="flex-none relative overflow-hidden thumbnail_card" style="--thumbnail-aspect-ratio: {{ $thumbnailAspectRatio }}">
      <img
        src="{{ . }}"
        role="presentation"
        loading="lazy"
        decoding="async"
        class="not-prose absolute inset-0 w-full h-full object-cover">
    </div>
  {{ end }}
  {{ if and .Draft .Site.Params.article.showDraftLabel }}
    <span class="absolute top-0 right-0 m-2">
      {{ partial "badge.html" (i18n "article.draft" | emojify) }}
    </span>
  {{ end }}
  {{/* skip */}}
</article>

完全把 Hugo 資料處理和 HTML 渲染解耦,後續就只是檢查變數、套用變數而已,而不是在 HTML 結構裡面塞 Hugo 處理邏輯。

BTW

附帶一提,給 AI 看這篇他就會開始跟你說 DRY 多重要,這篇確實印證了 DRY 但那還是要看情況,胡亂 DRY 只是把所有混亂從 ABCDE 搬到 X 而已,混亂還是一樣存在,不過 blowfish 這段調用確實是真的該合併做 DRY 的場景。