继续我们的博客优化之旅,本篇内容我们将介绍如何使用Hugo
实现响应式和优化的图片。
在之前文章里,通过腾讯云数据万象实现了图片优化能力,具体的可参考文章累计布局偏移修复方案改进 —— 自动生成图片宽高。
经过一段运行后,发现这里有一个弊端。
Run hugo --gc --minify --cleanDestinationDir
Start building sites …
hugo v0.119.0-b84644c008e0dc2c4b67bd69cccf87a41a03937e linux/amd64 BuildDate=2023-09-24T15:20:17Z VendorInfo=gohugoio
ERROR Failed to get JSON resource "https://static.***.com/64412246-9050f100-d0c1-11e9-893a-f9b0766533ad.png?imageInfo&t=1698674110": Get "https://static.***.com/64412246-9050f100-d0c1-11e9-893a-f9b0766533ad.png?imageInfo&t=1698674110": stream error: stream ID 1; STREAM_CLOSED; received from peer
ERROR Failed to get JSON resource "https://static.***.com/SkRx5uFwQ8Cliyq.jpg?imageInfo&t=1698674110": Get "https://static.***.com/SkRx5uFwQ8Cliyq.jpg?imageInfo&t=1698674110": stream error: stream ID 3; STREAM_CLOSED; received from peer
随着图片数量增多,因为需要调接口查询图片信息,这里构建耗时变长,同时也特别容易出现超时导致构建失败。
失败的时候,需要手动重跑构建,自动化发布卡壳了。
经过一番搜索,发现其实Hugo
本身是支持图片处理能力的。
Image processing
Resize, crop, rotate, filter, and convert images.
下面以我使用的PaperMod主题为例,讲下如何通过image processing
实现图片响应式优化。
image processing
需要用到Page bundles
,所以文章目录结构需要调整,
将一篇文章的资源(md文件,图片等)放在一个目录下。
content/
├── posts
│ ├── my-post
│ │ ├── content1.md
│ │ ├── content2.md
│ │ ├── image1.jpg
│ │ ├── image2.png
│ │ └── index.md
│ └── my-other-post
│ └── index.md
目录结构调整完毕后,接下来修改图片显示文件代码。
这里需要生成webp
格式图片,所以需要使用hugo
的extended版本。
PagerMod
主题涉及到图片显示的一共三个文件:
_default/_markup/render-image.html,对应markdown图片语法解析。
{{- $respSizes := slice 480 720 1080 -}} /*生成的图片规格*/
{{- $dataSzes := "(min-width: 768px) 720px, 100vw" -}}
{{- $holder := "GIP" -}}
{{- $hint := "photo" -}}
{{- $filter := "box" -}}
{{- $Destination := .Destination -}}
{{- $Text := .Text -}}
{{- $Title := .Title -}}
/*内容图片响应式开关配置,默认为true*/
{{- $responsiveImages := (.Page.Params.responsiveImages | default site.Params.responsiveImages) | default true }}
{{ with $src := .Page.Resources.GetMatch .Destination }}
{{- if $responsiveImages -}}
<picture>
/*只有使用了hugo扩展版本的,才生成webp格式图片*/
{{- if and hugo.IsExtended (ne $src.MediaType.Type "image/webp") -}}
<source type="image/webp" srcset="
{{- with $respSizes -}}
{{- range $i, $e := . -}}
{{- if $i }}, {{ end -}}{{- ($src.Resize (print . "x webp " $hint " " $filter) ).RelPermalink | absURL }} {{ . }}w
{{- end -}}
{{- end -}}" sizes="{{ $dataSzes }}" />
{{- end -}}
<source type="{{ $src.MediaType.Type }}" srcset="
{{- with $respSizes -}}
{{- range $i, $e := . -}}
{{- if ge $src.Width . -}}
{{- if $i }}, {{ end -}}{{- ($src.Resize (print . "x jpg " $filter) ).RelPermalink | absURL}} {{ . }}w
{{- end -}}
{{- end -}}
{{- end -}}, {{$src.Permalink }} {{printf "%dw" ($src.Width)}}" sizes="{{ $dataSzes }}" />
<img src="{{ $Destination | safeURL }}" width="{{ .Width }}" height="{{ .Height }}" alt="{{ $Text }}" title="{{ $Title }}" loading="lazy" />
</picture>
{{- else }}
<img src="{{ $Destination | safeURL }}" width="{{ $src.Width }}" height="{{ $src.Height }}" alt="{{ $Text }}" title="{{ $Title }}" loading="lazy" />
{{- end }}
{{ end }}
partials/cover.html,对应文章封面解析。
{{- $respSizes := slice 480 720 1080 -}}
{{- $dataSzes := "(min-width: 768px) 720px, 100vw" -}}
{{- $holder := "GIP" -}}
{{- $hint := "photo" -}}
{{- $filter := "box" -}}
{{- with .cxt}} {{/* Apply proper context from dict */}}
{{- if (and .Params.cover.image (not $.isHidden)) }}
{{- $alt := (.Params.cover.alt | default .Params.cover.caption | plainify) }}
<figure class="entry-cover">
/*封面响应式图片配置开关,默认为true*/
{{- $responsiveImages := (.Params.cover.responsiveImages | default site.Params.cover.responsiveImages) | default true }}
{{- $addLink := (and site.Params.cover.linkFullImages (not $.IsHome)) }}
{{- $cover := (.Resources.ByType "image").GetMatch (printf "*%s*" (.Params.cover.image)) }}
{{- if $cover -}}{{/* i.e it is present in page bundle */}}
{{- if $addLink }}<a href="{{ (path.Join .RelPermalink .Params.cover.image) | absURL }}" target="_blank"
rel="noopener noreferrer">{{ end -}}
{{- if $responsiveImages -}}
<picture>
{{- if and hugo.IsExtended (ne $cover.MediaType.Type "image/webp") -}}
<source type="image/webp" srcset="
{{- with $respSizes -}}
{{- range $i, $e := . -}}
{{- if $i }}, {{ end -}}{{- ($cover.Resize (print . "x webp " $hint " " $filter) ).RelPermalink | absURL }} {{ . }}w
{{- end -}}
{{- end -}}" sizes="{{ $dataSzes }}" />
{{- end -}}
<source type="{{ $cover.MediaType.Type }}" srcset="
{{- with $respSizes -}}
{{- range $i, $e := . -}}
{{- if ge $cover.Width . -}}
{{- if $i }}, {{ end -}}{{- ($cover.Resize (print . "x jpg " $filter) ).RelPermalink | absURL}} {{ . }}w
{{- end -}}
{{- end -}}
{{- end -}}, {{$cover.Permalink }} {{printf "%dw" ($cover.Width)}}" sizes="{{ $dataSzes }}" />
<img loading="lazy" src="{{ $cover.Permalink }}" alt="{{ $alt }}"
width="{{ $cover.Width }}" height="{{ $cover.Height }}">
</picture>
{{- else }}{{/* Unprocessable image or responsive images disabled */}}
<img loading="lazy" src="{{ (path.Join .RelPermalink .Params.cover.image) | absURL }}" alt="{{ $alt }}">
{{- end }}
{{- else }}{{/* For absolute urls and external links, no img processing here */}}
{{- if $addLink }}<a href="{{ (.Params.cover.image) | absURL }}" target="_blank"
rel="noopener noreferrer">{{ end -}}
<img loading="lazy" src="{{ (.Params.cover.image) | absURL }}" alt="{{ $alt }}">
{{- end }}
{{- if $addLink }}</a>{{ end -}}
{{/* Display Caption */}}
{{- if not $.IsHome }}
{{ with .Params.cover.caption }}<p>{{ . | markdownify }}</p>{{- end }}
{{- end }}
</figure>
{{- end }}{{/* End image */}}
{{- end -}}{{/* End context */ -}}
shortcodes/figure.html,对应文章内figure
语法解析。
{{- $respSizes := slice 480 720 1080 -}}
{{- $dataSzes := "(min-width: 768px) 720px, 100vw" -}}
{{- $holder := "GIP" -}}
{{- $hint := "photo" -}}
{{- $filter := "box" -}}
{{ $src := .Get "src" }}
{{ $align := .Get "align" }}
{{ $alt := .Get "alt" }}
{{ $caption := .Get "caption" }}
/*内容图片响应式开关配置,默认为true*/
{{- $responsiveImages := (.Page.Params.responsiveImages | default site.Params.responsiveImages) | default true }}
<figure{{ if or (.Get "class") (eq (.Get "align") "center") }} class="
{{- if eq (.Get "align") "center" }}align-center {{ end }}
{{- with .Get "class" }}{{ . }}{{- end }}"
{{- end -}}>
{{- if .Get "link" -}}
<a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
{{- end }}
{{ with $src := $.Page.Resources.GetMatch (.Get "src") }}
<picture>
{{- if $responsiveImages -}}
{{- if and hugo.IsExtended (ne $src.MediaType.Type "image/webp") -}}
<source type="image/webp" srcset="
{{- with $respSizes -}}
{{- range $i, $e := . -}}
{{- if $i }}, {{ end -}}{{- ($src.Resize (print . "x webp " $hint " " $filter) ).RelPermalink | absURL }} {{ . }}w
{{- end -}}
{{- end -}}" sizes="{{ $dataSzes }}" />
{{- end -}}
<source type="{{ $src.MediaType.Type }}" srcset="
{{- with $respSizes -}}
{{- range $i, $e := . -}}
{{- if ge $src.Width . -}}
{{- if $i }}, {{ end -}}{{- ($src.Resize (print . "x jpg " $filter) ).RelPermalink | absURL}} {{ . }}w
{{- end -}}
{{- end -}}
{{- end -}}, {{$src.Permalink }} {{printf "%dw" ($src.Width)}}" sizes="{{ $dataSzes }}" />
{{- end }}
<img loading="lazy" src="{{ $src }}{{- if eq ($align) "center" }}#center{{- end }}"
{{- if or ($alt) ($caption) }}
alt="{{ with $alt }}{{ . }}{{ else }}{{ $caption | markdownify| plainify }}{{ end }}"
{{- end -}}
{{- with $src.Width -}} width="{{ . }}"{{- end -}}
{{- with $src.Height -}} height="{{ . }}"{{- end -}}
/> <!-- Closing img tag -->
</picture>
{{ end }}
{{- if .Get "link" }}</a>{{ end -}}
{{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}}
<figcaption>
{{ with (.Get "title") -}}
{{ . }}
{{- end -}}
{{- if or (.Get "caption") (.Get "attr") -}}<p>
{{- .Get "caption" | markdownify -}}
{{- with .Get "attrlink" }}
<a href="{{ . }}">
{{- end -}}
{{- .Get "attr" | markdownify -}}
{{- if .Get "attrlink" }}</a>{{ end }}</p>
{{- end }}
</figcaption>
{{- end }}
</figure>
正常插入jpg/png
图片,构建后会自动生成webp/原始格式下不同规格的图片。
提示:
随着图片数量增多,可能会遇到构建超时的错误,类似下述信息:
Start building sites …
hugo v0.96.0+extended darwin/arm64 BuildDate=unknown
Error: Error building site: "/Users/dondonliu/Code/liudon.github.io/content/posts/xxxx/index.md:1:1": timed out initializing value. You may have a circular loop in a shortcode, or your site may have resources that take longer to build than the `timeout` limit in your Hugo config file.
Built in 22356 ms
可以通过修改配置文件config.yml
,新增timeout
配置,调大超时时间解决。
buildDrafts: false
buildFuture: false
buildExpired: false
timeout: 60s // 调大此处的时间即可
终于知道为啥PagerMod
主题默认只有封面下才有生成不同规格的逻辑了。