製作一個 Hugo + Tailwind CSS 初始檔
Tags: hugo, tailwindcss, webdev, tutorial
Published: 2022-07-20 03:04:47

## 本篇要解決的問題

之前看了 [Hugo 的教學](https://www.letswrite.tw/hugo-basic/)，覺得這套 SSG 的工具不錯用，再加上最近租的主機要到期了，一些文章數很少的站，或是最近準備再寫幾篇文章的站，打算從 WordPress 脫離到 Hugo 上，並把產生出的靜態檔直接放在 GitHub Pages，做出一個免費的站。

因為有二個站想搬，就想先做一個 Hugo + Tailwind CSS 的初始檔出來，讓二個站 clone 初始檔後直接改樣式就可以上線。

結果不做不知道，一做不得了，好多的眉眉角角都是教學裡沒有的，得去從茫茫的文件裡去尋找，這篇就是把在做初始檔時遇到的這些問題，相關的解決方式給整理起來，相信是跟 August 一樣剛踏入 Hugo 宇宙的朋友們也會遇到的，希望可以減少大家踩坑時間。

最後也會附上初始檔的 GitHub 專案連結。

---

## 初始檔：安裝、開發、產靜態檔

這邊先寫一下怎麼使用這個初始檔。

先進到專案的頁面下載整包專案：[https://github.com/letswritetw/letswrite-hugo-tailwindcss-init](https://github.com/letswritetw/letswrite-hugo-tailwindcss-init)

下載前請點擊星星給 Let’s Write 一個鼓勵，這個初始檔真的做了好幾天。

確定你的本機有安裝 [Node.js](https://nodejs.org/zh-tw/download/)，沒有的話請先安裝，接著繼續安裝 [Yarn](https://yarnpkg.com/) 的話會更好。

專案裡開啟終端機，輸入：

```bash
$ npm install
或
$ yarn
```

package.json 都寫好了，執行上面的 Command 就會安裝 Tailwind CSS。

要開發、要產出靜態檔也都寫好了，一樣用 Command 來執行。

**開發**

```bash
$ npm run serve
或
$ yarn serve
```

**產靜態檔**

```bash
$ npm run build
或
$ yarn build
```

為了放在 GitHub Pages 上，所以產出的靜態檔會是「docs」這個資料夾。

---

## config

config 檔，因為想讓自己習慣 [YAML](https://www.letswrite.tw/yaml-basic/)，所以檔案為 config.yml。

### Sitemap

文件：[Sitemap Templates](https://gohugo.io/templates/sitemap-template/)

config 檔可以設定輸出 Sitemap 檔，不過之前上 SEO 的講座時，其實有一種說法是：如果導覽列夠健全，Sitemap 就沒這麼重要。但說法是說法，既然可以自動產生了，就還是建一建。

```yaml
sitemap:
  changefreq: weekly
  filename: sitemap.xml
  priority: 1.0
```

`changefreq` 是指頁面的更新頻率，共有以下的值可以寫：

```yaml
always, hourly, daily, weekly, monthly, yearly, never
```

當然，大家常常都馬寫 always，priority 都馬寫 1.0，這就是為什麼 Sitemap的重要程度會愈來愈低吧，就跟以前塞 meta keywords 一樣。

寫在 config 檔是全站共用的，如果想要有單獨的頁面不一樣，就寫在頁面檔的 Front Matter 就好。

### Google Analytics

config 可以寫入 GA 的追蹤 ID，再加上內建的模版，Hugo 就會埋入 GA 追蹤碼。

**config**

```yaml
googleAnalytics: GA 追蹤碼 ID
```

在要埋入的地方加入以下 HTML，本專案是加在 layouts/partials/head.html 裡：

```html
{{ template "_internal/google_analytics.html" . }}
```

因為 2023 年 7 月後，Google 就要全面只能用 GA4，因此這邊裝的是 GA4 的 HTML。

但如果要放多個 GA 呢？無解，文件上沒有寫。

如果要放多個，建議直接在 head 裡手動放 Google Tag Manager 的代碼，改由 GTM 來塞 GA 追蹤碼。

### 客製參數

Let’s Write 製作的這份初始檔，有二個客製的參數是寫在 config 裡的：

```yaml
params:
  mainColor: '#42A6F7'
  favicon: 這邊填寫 favicon 的圖檔路徑
```

`mainColor` 會用在 theme-color、msapplication-TileColor 這二個 meta 上，一個是手機的瀏覽器功能條的顏色，一個是存成 Windows App 時會有的顏色，其實不太重要，但要注重細節的話就是也寫一下，把自己品牌的主色系碼給填上去。

第二個就是 favicon 的路徑。 

---

## head 檔

layouts/partials/head.html 這個檔，是頁面 <head> 裡的內容。在這裡踩到的坑有以下幾點。

### Open Graph

文件：[Open Graph](https://gohugo.io/templates/internal/#open-graph)

Open Graph 主要是 <head> 裡放社群分享時的卡片資訊，一般是用在 FB、LINE，Hugo 在 Front Matter 裡可以設定標題、描述、配圖、日期、影音、分類。

Let’s Write 的初始檔分別為以下：

```markdown
title: 標題
description: 描述
cover: 配圖
date: 日期
categories:
	- 分類
videos:
	- 影片網址
```

categories 影響的是頁面裡的 see also，中文可以翻作「你可能有興趣」、「看過這篇的人也看過」之類的。文件裡用的是 series，但這邊實作 categories 也有同樣效果。

各頁的 Front Matter 設好後，在模版檔上要放入 Open Graph 的模版，這個是內建的模版：

```html
{{ template "_internal/opengraph.html" . }}
```

### 引用 CSS、JS 檔

文件：[Hugo Pipes Introduction](https://gohugo.io/hugo-pipes/introduction/)、[JavaScript Building](https://gohugo.io/hugo-pipes/js/)

引用 CSS、JS 這段真的是踩了一個坑，主要是雖然 Hugo 開發時，檔案一存檔後本機的 Demo 網址就會即時更新，但 CSS、JS 是有快取的，不清快取即便重整也會一直看到舊的 CSS 跟 JS。

後來看了文件，才知道 Hugo 有設定了 fingerprint 的功能，可以讓引用的 CSS、JS 檔的檔名每次都不一樣。

將 CSS 檔放進 **assets/css/** 的資料夾中，JS 檔放進 **assets/js/** 的資料夾中。主要是放 **assets** 這個資料夾裡的檔案才能用 resource 的方式去抓取。

```html
<!-- 引用 CSS -->
{{ $tailwind := resources.Get "css/tailwind.min.css" | fingerprint }}
<link rel="stylesheet" href="{{ $tailwind.Permalink }}">

<!-- 引用 JS -->
{{ $main := resources.Get "js/main.js" | js.Build "main.js" | minify | fingerprint }}
<script src="{{ $main.Permalink }}"></script>
```

CSS 的部份，Hugo 可以直接編譯 SASS/SCSS，但因為本初始檔用的是 Tailwind CSS，因此就加上 `fingerprint` 讓檔名補上亂數就行。

JS 的部份，Hugo 可以編譯 ES6+ 耶~ 還可以壓縮檔案，所以這邊就是寫編譯 + 壓縮 + 加亂數。

---

## Schema 結構化資料

這塊是 SEO 用的，完整的文件請看 Google：[瞭解結構化資料的運作方式](https://developers.google.com/search/docs/advanced/structured-data/intro-structured-data)。

layouts/partials/schema.html 裡有放了一個基本的「[標誌](https://developers.google.com/search/docs/advanced/structured-data/logo)」：

```html
<script type="application/ld+json">
  {
    "@context": "https://www.schema.org",
    "@type": "Organization",
    "name": "{{ .Site.Title }}",
    "url": "{{ .Site.BaseURL }}",
    "logo": "{{ .Site.Params.favicon }}"
  }
</script>
```

然後在 baseof.html 上用 partial 的引用：

```html
{{ partial "schema.html" . }}
```

這種作法是參考這篇：[Adding Structured Data to your Hugo site](https://dev.to/pdwarkanath/adding-structured-data-to-your-hugo-site-58db)

可是很奇怪的是，在 Hugo 的文件裡，內建模版裡原本就有 Schema 的：

```html
{{ template "_internal/schema.html" . }}
```

只是文件上沒有說明到底要怎麼使用，要加什麼參數才會有，August 直接引用後是沒有吐出什麼程式碼出來，因此才改用 partial 的方式。

知道內建模版關於 Schema 的朋友歡迎留言提供~

---

## content 檔案架構

content 裡放的是每個文章的頁面，檔案裡的架構是這樣：

```
content
├── post
│   ├── demo1
│   │   ├── cover.png
│   │   └── index.md
│   ├── demo2
│   │   ├── cover.png
│   │   └── index.md
│   └── index.md（posts 的列表首頁）
└── index.md（網站首頁）
```

像 demo1，每一篇文章都是收在一個資料夾裡，裡面有 index.md 是內文，還有一個圖檔是配圖。

都放在同一個資料夾裡，要抓配圖的方式就是：

```html
{{ $image := .Resources.GetMatch "xxx.jpg" }}
```

另外也可以統一將所有圖檔放進一個資料夾裡，這個資料夾必須位在 assets 裡，像 assets/images/。要抓配圖的方式就是：

```html
{{ $image := resources.Get "images/xxx.jpg" }}
```

但這個方式，August 不管怎麼試就是失敗，一直抓不到圖檔的寬高，所以本初始檔是用第一種圖檔跟文章都放在同一個資料夾的方式。

圖檔也可以是外面的圖庫，抓配圖的方式就是：

```html
{{ $image := resources.GetRemote "https://xxx.xxx/xxx.png" }}
```

抓了配圖後，可以抓到圖檔的路徑、寬度、高度，範例如下：

```html
{{ $image := .Resources.GetMatch "xxx.jpg" }}
{{ with $image }}
  <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}">
{{ end }}
```

### 圖檔抓文章連結、裁切

layouts/partials/cover.html 這個檔有做圖檔的引用、裁切，主要是處理文章配圖。

Hugo 除了引用圖檔，也可以設定一個圖片尺寸，會自動將圖裁成設定的寬。

這邊也踩了一個坑，主要是加入文章連結的部份，上面的範例 code，用了 `{{ .RelPermalink }}` 去抓圖片路徑，不幸的是，文章連結這個變數也是用 `RelPermalink`，解決方式就是先將文章連結存成一個變數。

整合抓文章連結、裁切圖片，範例的程式碼可以改成以下：

```html
{{ $Uri := .RelPermalink }}
{{ $Image := .Resources.GetMatch (print .Params.cover) }}
{{ $ImageResize := $Image.Resize "768x" }}
{{ with $Image }}
  <div>
    <a href="{{ $Uri }}">
      <img
        src="{{ $ImageResize.RelPermalink }}" alt="{{ .Title }}"
        width="{{ $ImageResize.Width }}" height="{{ $ImageResize.Height }}">
        </a>
  </div>
{{ end }}
```

完整的說明可以看文件：[Image Processing](https://gohugo.io/content-management/image-processing/)

---

## 未完待續

目前 August 完成的是初始的檔案，But，是還沒實際使用過的，之後會開始拿這份檔實際把手上的小站搬家看看，如果操作的過程中覺得目前架構或設計有不順的，就會一併修改。

初始檔 GitHub 專案的連結在這，取用前麻煩點個星星，或分享本篇文章，你的小小動作對本站都是大大的鼓勵：

[https://github.com/letswritetw/letswrite-hugo-tailwindcss-init](https://github.com/letswritetw/letswrite-hugo-tailwindcss-init)