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>
- 毫無排版極度難以閱讀,有些 if-else 換行,有些不換行,沒有 indent 連續 30 行的 if-end/with-end/range-end 根本找不到對應關係
{{- -}}與{{ }}混用且無規律- Go template 資料處理邏輯和 HTML 外觀結構耦合
- 錯誤不合規的
og:image - 型別混用
- 處理邏輯混亂,不尊重 front matter 設定,反而先看檔案名稱
- 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 的場景。