Hugo 博客初始化过程记录

需求

hugo 编写文档时,需要手工将图片复制到 /static/images目录下,会很繁琐,而且在terminall 页面编辑不一定体验感会很强,尤其是在文章的复制粘贴的时候,又携带了很多图片,会变得非常不方便

Obsidian

Obsidian 是一个开源的 markdown 笔记本,有丰富的插件,支持将笔记定时到github等,页面也非常友好,我们可以使用 obsidian 编辑我们的文章,同时还可以解决图片编辑的问题,以下是详细步骤

  • 修正图片链接 设置中找到 Files and link 关闭Use[[Wikilinks]] 如果不关闭复制上来的图片格式会是:![[]] 而不是markdown格式的![]() hugo 也就无法正确的加载
  • 设置文件保存路径 设置中指定文件的保存路径为: /static/images 与hugo一致
  • 使用相对路径save New link format 要改为:Absolute path in vault 默认是短链,hugo也无法识别

配置

obsidian配置完后会发现图片还是无法正常加载,因为 hugo 编译的时候会把 /static/images 的东西编译到 public 目录,页面引用路径会变成:/images 编辑文件(没有则创建):vim layouts/_default/_markup/render-image.html

{{- $src := .Destination -}}
{{- $src = replace $src "static/" "/" -}}
<img src="{{ $src }}" alt="{{ .Text }}" {{ with .Title }}title="{{ . }}"{{ end }} />

这个渲染钩子会自动将 Markdown 中的 static/images/ 路径转换为 /images/。 这是一个 Hugo 内置功能,不需要修改 Markdown 文件本身,能自动处理页面渲染时的路径问题。

Hugo 有强大的图片处理能力,可以在构建时自动压缩和调整图片大小: 在render-image.html 中引入如下内容:(这段已经包含了路径替换,与上一段不能一起使用)

{{- $src := .Destination -}}
{{- $src := replace $src "static/" "/" -}}
{{- $original := printf "static%s" $src -}}
{{- if fileExists $original -}}
  {{- $resource := resources.Get (strings.TrimPrefix "/" $src) -}}
  {{- with $resource -}}
    {{- $processed := .Resize "1000x webp q80" -}}
    <img src="{{ $processed.RelPermalink }}" alt="{{ $.Text }}" />
  {{- else -}}
    <img src="{{ $src }}" alt="{{ $.Text }}" />
  {{- end -}}
{{- else -}}
  <img src="{{ $src }}" alt="{{ $.Text }}" />
{{- end -}}

*不推荐使用,因为路径的关系 resource.Get会失效 *

在.git/hook/pre-commit 写入如下内容(write by cursor),并 chmod +x precommit 注意:要预装:ImageMagick brew install image magick 即可

#!/bin/bash

# 彩色输出函数
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m' # 恢复默认颜色

# 创建临时文件
TMP_FILE=$(mktemp)

echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE}📸 图片压缩工具${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"

# 检查有哪些工具可用
TOOLS_FOUND=0
if command -v pngquant &> /dev/null; then
    echo -e "${GREEN}${NC} 发现 pngquant"
    TOOLS_FOUND=$((TOOLS_FOUND+1))
fi
if command -v optipng &> /dev/null; then
    echo -e "${GREEN}${NC} 发现 optipng"
    TOOLS_FOUND=$((TOOLS_FOUND+1))
fi
if command -v jpegoptim &> /dev/null; then
    echo -e "${GREEN}${NC} 发现 jpegoptim"
    TOOLS_FOUND=$((TOOLS_FOUND+1))
fi
if command -v convert &> /dev/null || command -v magick &> /dev/null; then
    CONVERT_CMD=$(command -v magick 2>/dev/null || echo "convert")
    echo -e "${GREEN}${NC} 发现 ImageMagick ($CONVERT_CMD)"
    TOOLS_FOUND=$((TOOLS_FOUND+1))
fi
if [ $TOOLS_FOUND -eq 0 ]; then
    echo -e "${RED}错误: 未找到任何图片压缩工具${NC}"
    echo "请安装以下至少一种工具:"
    echo "  • ImageMagick (convert/magick)"
    echo "  • pngquant"
    echo "  • optipng"
    echo "  • jpegoptim"
    exit 1
fi

echo -e "\n${BLUE}🔍 查找需要压缩的图片...${NC}"
# 只获取新增(A)和修改(M)的文件
git diff --cached --name-only --diff-filter=AM -z > "$TMP_FILE"

# 统计数量和大小
TOTAL_FILES=0
TOTAL_ORIG_SIZE=0
TOTAL_NEW_SIZE=0
PROCESSED_FILES=0

# 处理文件
while IFS= read -r -d $'\0' FILE; do
    # 检查文件是否存在且是images目录下的图片
    if [[ -f "$FILE" && "$FILE" == static/images/* ]]; then
        # 获取小写扩展名
        EXT="${FILE##*.}"
        EXT=$(echo "$EXT" | tr '[:upper:]' '[:lower:]')
        if [[ "$EXT" == "jpg" || "$EXT" == "jpeg" || "$EXT" == "png" ]]; then
            TOTAL_FILES=$((TOTAL_FILES+1))
            # 获取文件名部分
            FILENAME=$(basename "$FILE")
            if [[ "$EXT" == "jpg" || "$EXT" == "jpeg" ]]; then
                echo -e "\n${YELLOW}🖼️  处理 JPEG:${NC} $FILENAME"
                cp "$FILE" "$FILE.bak"
                if command -v jpegoptim &> /dev/null; then
                    echo -e "   使用 jpegoptim 优化中..."
                    jpegoptim --max=82 --strip-all --all-progressive --overwrite "$FILE" > /dev/null
                elif command -v convert &> /dev/null || command -v magick &> /dev/null; then
                    echo -e "   使用 ImageMagick 优化中..."
                    $CONVERT_CMD "$FILE" -resize "1200x>" -quality 82 -strip "$FILE" > /dev/null
                fi
            elif [[ "$EXT" == "png" ]]; then
                echo -e "\n${YELLOW}🖼️  处理 PNG:${NC} $FILENAME"
                cp "$FILE" "$FILE.bak"
                if command -v convert &> /dev/null || command -v magick &> /dev/null; then
                    echo -e "   使用 ImageMagick 调整大小..."
                    $CONVERT_CMD "$FILE" -resize "1200x>" -strip "$FILE.tmp.png" > /dev/null
                    mv "$FILE.tmp.png" "$FILE"
                fi
                if command -v pngquant &> /dev/null; then
                    echo -e "   使用 pngquant 优化中..."
                    pngquant --force --quality=80-95 --skip-if-larger --strip --output "$FILE" "$FILE" > /dev/null 2>&1
                fi
                if command -v optipng &> /dev/null; then
                    echo -e "   使用 optipng 优化中..."
                    optipng -o3 -quiet "$FILE" > /dev/null
                fi
            fi
            # 检查效果
            ORIG_SIZE=$(stat -c%s "$FILE.bak" 2>/dev/null || stat -f%z "$FILE.bak")
            NEW_SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
            
            ORIG_SIZE_KB=$(( $ORIG_SIZE / 1024 ))
            NEW_SIZE_KB=$(( $NEW_SIZE / 1024 ))
            
            if [ $NEW_SIZE -lt $ORIG_SIZE ]; then
                SAVED_KB=$(( $ORIG_SIZE_KB - $NEW_SIZE_KB ))
                PERCENT=$(( ($SAVED_KB * 100) / ($ORIG_SIZE_KB + 1) ))
                echo -e "   ${GREEN}✓ 压缩成功:${NC} ${ORIG_SIZE_KB}KB → ${NEW_SIZE_KB}KB (减小 ${SAVED_KB}KB, 节省 ${PERCENT}%)"
                rm "$FILE.bak"
                git add "$FILE"
                
                PROCESSED_FILES=$((PROCESSED_FILES+1))
                TOTAL_ORIG_SIZE=$((TOTAL_ORIG_SIZE + ORIG_SIZE_KB))
                TOTAL_NEW_SIZE=$((TOTAL_NEW_SIZE + NEW_SIZE_KB))
            else
                echo -e "   ${RED}✗ 无法有效压缩,保留原图${NC}"
                mv "$FILE.bak" "$FILE"
            fi
        fi
    fi
done < "$TMP_FILE"
# 清理临时文件
rm "$TMP_FILE"

echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
if [ $TOTAL_FILES -eq 0 ]; then
    echo -e "${YELLOW}未找到需要处理的图片文件${NC}"
else
    echo -e "${GREEN}🎉 总结:${NC}"
    echo -e "  • 找到 ${TOTAL_FILES} 个图片文件"
    echo -e "  • 成功压缩 ${PROCESSED_FILES} 个文件"
    
    if [ $PROCESSED_FILES -gt 0 ]; then
        TOTAL_SAVED=$((TOTAL_ORIG_SIZE - TOTAL_NEW_SIZE))
        TOTAL_PERCENT=$((TOTAL_SAVED * 100 / TOTAL_ORIG_SIZE))
        echo -e "  • 总共节省空间: ${TOTAL_SAVED}KB (${TOTAL_PERCENT}%)"
    fi
fi
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"

exit 0

最终效果图:

我们引用的DoIt 主题已经包含了很多评论工具,如 giscus/disqus等等,现主要介绍这两种,具体的可以在官网中查找:site_configuration 二者的接入方式都比较简单,此处不再展开,要注意的是一定要参考官方文档,插件都更新的比较快,以官方文档为准

baseURL = 'https://ox-warrior.top/'
languageCode = 'en-us'
title = 'Ox-Warrior'
theme = 'DoIt'

[params.page.comment]
  enable = true
  [params.page.comment.disqus]
    enable = true
    shortname = "xxxxxxx"
  [params.page.comment.giscus]
    enable = true
    dataRepo = "ox-warrior/hugo-doit-giscus"
    dataRepoId = "Rxxxxx"
    dataCategory = "General"
    dataCategoryId = "DIC_xxxxxx"
    dataMapping = "pathname"
    dataReactionsEnabled = "1"
    dataTheme = "preferred_color_scheme"
    dataInputPosition = "bottom"

[build]
  noComments = false

这里同时开启了 giscusdisqus 如果不加任何配置,会优先使用第一个:disqus 我们现在有个需求:英文文档使用 disqus (外网访问较快),中文使用 giscus,如果要实现该效果,我们需要在文章的开头属性那里配置如下

comment:
  disqus:
    enable: true
  giscus:
-   enable: false

需要将每一个插件都穷举在这里,很不方便,通过对 DoIt 的分析,发现控制comment的加载是通过 themes/DoIt/layouts/_partials 下的 comment.html控制的,那可以修改这里的代码,增加一个参数commentSystem 根据他的值来决定加载哪个! 但是我们最好不要直接修改源代码,可以在项目的 layouts/partials 下创建一个新的comment.html

{{- $cdn := .Scratch.Get "cdn" | default dict -}}
{{- $fingerprint := .Scratch.Get "fingerprint" -}}
{{- $comment := .Scratch.Get "comment" | default dict -}}
{{- $commentConfig := dict -}}
{{- if $comment.enable -}}
    {{- .Scratch.Set "enableComment" true -}}
    <div id="comments" class="print:!tw-hidden tw-pt-32 tw-pb-8">
        {{- /* Disqus Comment System */ -}}
        {{- /* 使用 .Params.commentSystem 参数来决定启用哪个评论系统 */ -}}
        {{- $commentSystem := .Params.commentSystem | default "default" -}}
        {{- if eq $commentSystem "none" -}}
            <!-- 不显示评论,但保留评论区块 -->
            <div class="comment">评论功能已禁用</div>
        {{- else if eq $commentSystem "disqus" -}}
            {{- $disqus := $comment.disqus | default dict -}}
            {{- if $disqus.enable -}}
                <div id="disqus_thread" class="comment"></div>
                {{- $source := printf "https://%v.disqus.com/embed.js" $disqus.shortname -}}
                {{- dict "Source" $source "Defer" true | dict "Scratch" .Scratch "Data" | partial "scratch/commentScript.html" -}}
                <noscript>
                    Please enable JavaScript to view the comments powered by <a href="https://disqus.com/?ref_noscript">Disqus</a>.
                </noscript>
            {{- end -}}
// 其他插件方法类似,不一一展开了
        {{- end -}}

最后在文档的开头增加一个属性:commentSystem: “插件名” 即可如果没设置就是不开启

可以通过 Templater 插件定义一个专属属性头 准备一个.template 格式文件,插件安装好后导入即可。

---
date: <% tp.date.now("YYYY-MM-DDTHH:mm:ss") %>
draft: "false"
title: "<% tp.file.title %>"
commentSystem: giscus
---

创建新文档后,可以使用快捷键或者鼠标点击插件应用该 matter 就会自动追加到开头

换一次电脑,需要重新安装

git clone https://github.com/HEIGE-PCloud/DoIt.git themes/DoIt