Hugoの画像を自動で圧縮する方法

Markdown Render Hooksを使うと、MarkdownレンダラであるGoldmarkの挙動を上書きすることができます。 これによって、記事のソースファイルにShortcodeを入れたりせずに(つまりHugo無しに意味が通る状態で)<img>タグを差し替えられます。

また、Hugoには組み込みの画像圧縮機能があり、PNGなどの大きな画像ファイルをWebPといった軽量フォーマットに変換してくれます。 これらを組み合わせることで、記事の執筆環境を変えずにページの大幅な軽量化が実現できましたので共有します。

Image Processing

HugoではResizeメソッドを利用することで、サイズ変更 ・ フォーマット変換が行えます。

構文は次の通りで、横幅 ・ 高さをx区切り、その後変換先フォーマットを記述します。

{{ $image.Resize "WIDTHxHEIGHT FORMAT" }}

Markdown Render Hooks

Render Hookを登録するには、layouts/_default/_markup/内に対応するファイルを入れる必要があります。

画像のレンダリングを上書きする場合は、layouts/_default/_markup/render-image.htmlです。

このファイル内では次の変数を使うことができます。

変数名内容
PageレンダリングされるPageオブジェクト
Destination画像のURL
Titletitle属性
Textalt属性(ALTテキスト) ・ HTML
PlainTextalt属性(ALTテキスト) ・ プレーンテキスト

たとえば、このようなMarkdownがあったとすると、

![とても**美しい**絵](beautiful.png "秀逸な題名")

各変数の値は以下のようになります。

変数名内容
Page(書かれているPageオブジェクト)
Destination"beautiful.png"
Title"秀逸な題名"
Text"とても<strong>美しい</strong>絵"
PlainText"とても美しい絵"

画像をimgタグからpictureタグに差し替える

以上の内容を組み合わせたHookがこちらです。

{{ $sourceImage := .Page.Resources.GetMatch .Destination }}
{{ $webpImage := $sourceImage.Resize (printf "%dx%d webp" $sourceImage.Width $sourceImage.Height) }}
<picture>
  <source srcset="{{ $webpImage.RelPermalink }}" type="image/webp">
  <img src="{{ .Destination }}" width="{{ $sourceImage.Width }}" height="{{ $sourceImage.Height }}" alt="{{ .PlainText }}">
</picture>

まず、URL(.Destination)を利用して、.Pageから画像オブジェクトを取得し、.ResizeによってWebP形式に圧縮します。 その後、対応ブラウザで優先的にWebP形式の画像が呼ばれるように<picture>タグを配置しています。

どれくらい通信量を減らせたか

圧縮前圧縮後
圧縮前圧縮後
12.1MB402kB

(字が小さすぎて何のこっちゃですが)同じページを読み込むときの転送量が30分の1にまで小さくなりました! 元の画像が大きすぎたのを加味しても、容量が小さくなりすぎていて驚きました。キャッシュは無効にしてあります。

参考文献