以 Go + Echo 打造部落格|第 3 篇:模板與靜態資源(html/template + Tailwind CDN)
今天把畫面整理起來:用 html/template 做出 layout(版型)、partial(可重用片段),再掛上 Tailwind CDN 讓頁面秒變清爽。完成後,你會有首頁的基本視覺、乾淨的模板結構,之後要加表單、清單、分頁都好上手 😄
小辭典:
layout:共用外框(像房子的骨架),各頁把內容塞進去就好。
partial:可重複使用的小區塊(例如導覽列、頁腳)。
CDN(Content Delivery Network):放在全球伺服器的檔案,讓你直接引用,不用下載到專案裡。
layouts/
、partials/
、pages/
三層,讓結構清楚/
用模板渲染:標題、站名、現在時間mkdir -p web/templates/layouts web/templates/partials web/templates/pages
web/templates/layouts/base.html
(骨架)
{{ define "layouts/base" -}}
<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8">
<title>{{ block "title" . }}{{ .Title }} • {{ .SiteName }}{{ end }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Tailwind CDN(方便先上手;未來可改本地 build) -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 你自己的靜態檔(logo、favicon 等) -->
<link rel="icon" href="/static/favicon.ico">
</head>
<body class="min-h-screen bg-slate-50 text-slate-800">
{{ template "partials/nav" . }}
<main class="container mx-auto px-4 py-8">
{{ block "content" . }}{{ end }}
</main>
{{ template "partials/footer" . }}
</body>
</html>
{{- end }}
web/templates/partials/nav.html
(導覽)
{{ define "partials/nav" -}}
<header class="border-b bg-white">
<div class="container mx-auto px-4 py-4 flex items-center justify-between">
<a href="/" class="flex items-center gap-3 font-semibold text-slate-900 hover:opacity-80">
<img src="/static/logo.svg" alt="logo" class="w-8 h-8">
<span>{{ .SiteName }}</span>
</a>
<nav class="flex items-center gap-6 text-slate-600">
<a class="hover:text-slate-900" href="/">首頁</a>
<a class="hover:text-slate-900" href="/health">健康檢查</a>
<a class="hover:text-slate-900" href="https://echo.labstack.com/" target="_blank" rel="noreferrer">Echo</a>
</nav>
</div>
</header>
{{- end }}
web/templates/partials/footer.html
(頁腳)
{{ define "partials/footer" -}}
<footer class="border-t bg-white mt-12">
<div class="container mx-auto px-4 py-8 text-sm text-slate-500">
<p>© {{ .Year }} • {{ .SiteName }} • 以 Go + Echo 打造部落格</p>
</div>
</footer>
{{- end }}
web/templates/pages/index.html
(首頁內容)
{{ define "pages/index" -}}
{{ template "layouts/base" . }}
{{ define "title" -}}首頁{{ end }}
{{ define "content" -}}
<section class="grid gap-6">
<h1 class="text-3xl font-bold">👋 Hello Blog</h1>
<p class="text-slate-600">
這是你的最小可行首頁(MVP)。Tailwind 已掛好,之後我們會把文章列表、分頁、Tags 都加上來。
</p>
<div class="rounded-lg border bg-white p-4">
<div class="text-sm text-slate-500">台北時間</div>
<div class="text-xl font-mono">{{ .Now }}</div>
</div>
<div class="grid md:grid-cols-2 gap-4">
<a href="/health" class="rounded-lg border bg-white p-4 hover:shadow">
<div class="font-semibold">健康檢查(JSON)</div>
<div class="text-slate-500 text-sm">看看服務有沒有醒著 😴 → 😎</div>
</a>
<a href="/_ping" class="rounded-lg border bg白 p-4 hover:shadow">
<div class="font-semibold">Ping(純文字)</div>
<div class="text-slate-500 text-sm">只要回 pong 就代表連得上</div>
</a>
</div>
</section>
{{- end }}
{{- end }}
mkdir -p web/static
web/static/logo.svg
(超迷你示意)
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none">
<rect x="3" y="3" width="18" height="18" rx="4" stroke="#0ea5e9" stroke-width="2"/>
<path d="M7 12h10M12 7v10" stroke="#0ea5e9" stroke-width="2" stroke-linecap="round"/>
</svg>
web/static/favicon.ico
隨意放一個小圖示(如果沒有,也不會壞)。
html/template
讀得到多層資料夾把 cmd/server/main.go
的模板解析從 web/templates/*.html
改成 讀整個樹。
cmd/server/main.go
(完整可執行版本)
// ...(內容與教學相同,略)
internal/http/handlers/home.go
(更新:帶 SiteName、Year,渲染 pages/index)
// ...(內容與教學相同,略)
go-echo-blog/
├─ cmd/
│ └─ server/
│ └─ main.go ★ 更新(ParseGlob **/*.html、HomeHandlerWithMeta)
├─ internal/
│ ├─ http/
│ │ └─ handlers/
│ │ └─ home.go ★ 更新(提供 SiteName/Year,渲染 pages/index)
│ └─ storage/
│ └─ postgres/
│ └─ db.go
├─ web/
│ ├─ static/
│ │ ├─ logo.svg ★ 新增
│ │ └─ favicon.ico ★ 建議放
│ └─ templates/
│ ├─ layouts/
│ │ └─ base.html ★ 新增
│ ├─ partials/
│ │ ├─ nav.html ★ 新增
│ │ └─ footer.html ★ 新增
│ └─ pages/
│ └─ index.html ★ 新增
├─ migrations/
├─ docker-compose.yml
├─ .env
├─ .env.example
├─ Makefile
└─ go.mod
本篇 不需要遷移(主打版面與模板)。
啟動:
make run
首頁(HTML):
curl -i http://localhost:1323/ | head -n 15
檢查 Tailwind 是否載入:
curl -s http://localhost:1323/ | grep -o 'cdn.tailwindcss.com' || echo "not found"
健康檢查(JSON):
curl -s http://localhost:1323/health | jq .
Ping(純文字):
curl -s http://localhost:1323/_ping
# pong
ParseGlob("web/templates/**/*.html")
少了 **/*
就只會吃到單層。no such template "pages/index.html"
:檔名或資料夾層級打錯;或 define
名稱沒對到。首頁有 layout、partial、Tailwind,資料與畫面分工清楚 ✅
下一篇:文章 CRUD(API + 後台 UI)MVP:/admin/posts
新增/查/改/刪 + 表單驗證與 CSRF。