用 hugo render hooks 简化 markdown 中链接、图片的引用

背景

在 Obsidian 中链接自己的其他文章,Obsidian 能够正常跳转,但在 Hugo 中无法正确跳转,因为 Hugo 构造的链接路径有问题。之前在 Hugo Obsidian 结合实践 文章中有处理过图片引用的问题,但没有解决文章引用的问题。

搜索了下,这个帖子提供了解决方案,即利用 Markdown Render Hooks 修正 Hugo 生成的链接格式,同时也提供了一个允许相对路径引用的例子

开始之前

之前我在 Obsidian 中配置了链接使用绝对路径的格式,但在了解到可以通过 Render Hooks 来用相对路径引用资源后,我打算把 Obsidian 再改成使用相对路径格式。

原因主要是绝对路径有弊端:在 GitHub 仓库中或 VSCode 中查看文章,均无法展示图片,文章跳转也不正确。因为 Obsidian 的根目录只是 Hugo 项目的一个子目录,而非 Hugo 项目的根目录,绝对路径并不"绝对"。

那么把一些配置恢复原状即可:

  1. 在 Obsidian 中找到 New link format,修改为 Relative path to file
  2. 将 config.toml 中 mount static/assets 的代码删除
  3. Obsidian Linter 插件不再配置 Custom Regex Replacement 来给链接开头加上 /

接下来进入正题

Render Hooks 介绍

Render Hooks 这个概念并不复杂,其实就是让用户覆盖默认的渲染行为,自定义 markdown 渲染函数

Render Hooks 支持 image、link、heading、codeblock 这 4 种 markdown 元素类型

创建 Render Hooks 只需要在 layouts/_default/_markup 目录下创建名称为 render-{kind} 的 html 文件即可

txt 复制代码
layouts/
└── _default/
    └── _markup/
        ├── render-codeblock.html
        ├── render-heading.html
        ├── render-image.html
        └── render-link.html

接着看一下 Render Hooks 的内容,我们这里只关注 render-image.html 和 render-link.html,下面是 官网的两个例子

layouts/_default/_markup/render-link.html

html 复制代码
<a href="{{ .Destination | safeURL }}"{{ with .Title }} title="{{ . }}"{{ end }}{{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener"{{ end }}>
  {{ .Text | safeHTML }}
</a>

layouts/_default/_markup/render-image.html

html 复制代码
<p class="md__image">
  <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title }} title="{{ . }}"{{ end }} />
</p>

注:

  • 看懂以上代码需要先了解 Go Templates 的语法(Hugo Parameters 前面的小节都是重点)
  • 直接看 Go Templates 其实也会比较懵逼的,因为其中有用到一些 Go 语言的语法(比如 :=),文档里并没有讲,所以最好先了解一下 Go 的语法(推荐网站 Learn Go in Y Minutes

上面的代码用到了 variable、function、logic、context、pipe 的语法,把这些语法搞清楚,再看代码就很清晰了,这里就不解释了

实践:使用相对路径引用链接、图片

背景 部分已经给出了例子,但那个例子的代码比较难看懂,原因是:

  • 没有缩进,看不清楚逻辑关系
  • 有图片尺寸处理的代码,需要前置知识才能看懂,包括 Image ProcessingHugo Pipes Introduction
  • 不熟悉一些 Hugo functions 的功能

那么直接看我改造后的版本吧

render-link.html

html 复制代码
{{ $link := .Destination }}
{{ $isRemote := strings.HasPrefix $link "http" }}
{{- if not $isRemote -}}
  {{ $url := urls.Parse .Destination }}
  {{- if $url.Path -}}
    {{ $fragment := "" }}
    {{- with $url.Fragment }}{{ $fragment = printf "#%s" . }}{{ end -}}
    {{- with .Page.GetPage $url.Path }}{{ $link = printf "%s%s" .RelPermalink $fragment }}{{ end -}}
  {{- end -}}
{{- end -}}
<a href="{{ $link | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if $isRemote }} target="_blank"{{ end }}>{{ .Text | safeHTML }}</a>

注:

  • 区分 http 链接和本地链接 2 种情况
  • $fragment 表示锚点
  • 通过 .Page.GetPage.RelPermalink 拿到了最终的链接

render-image.html

html 复制代码
{{ $path := .Destination }}
{{ $isRemote := strings.HasPrefix $path "http" }}
{{- if not $isRemote -}}
  {{ $path = path.Join .Page.File.Dir .Destination }}
  {{ $path = printf "/%s" $path }}
{{- end -}}
<figure>
  <img src="{{ $path }}" alt="{{ $.Text }}" />
  {{ with $.Title | safeHTML }}
    <figcaption class="image-caption">{{- . -}}</figcaption>
  {{ end }}
</figure>

注:

  • 区分 http 图片和本地图片 2 种情况
  • $path 开头添加的 / 很重要,表示绝对路径。如果不加的话,会视为相对路径,渲染成 html 后会在前面自动加上当前文章的路径,导致无法访问到
  • DoIt 主题也有自定义 Render Hooks,我们在根目录下定义的 Render Hooks 会覆盖主题的,因此可能会丢失一些主题的功能。主题的 Render Hooks 代码有点复杂,没太看懂。
    测试发现功能没影响,但图片居中的样式丢了。看了下主题的 CSS 选择器,在 img 标签外面套上 figure 就好了。
相关推荐
小李小李不讲道理11 小时前
行动+思考 | 2024年度总结
前端·程序员·年终总结
聪小陈1 天前
圣诞节:记一次掘友让我感动的时刻
前端·程序员
百万蹄蹄向前冲2 天前
2024不一样的VUE3期末考查
前端·javascript·程序员
陈哥聊测试3 天前
软件格局在变,谁能扛起国产替代的大旗?
安全·程序员·产品
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 天前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
少年姜太公3 天前
从零开始详解js中的this(下)
前端·javascript·程序员
凌虚3 天前
Kubernetes APF(API 优先级和公平调度)简介
后端·程序员·kubernetes
小华同学ai4 天前
ShowDoc:Star12.3k,福利项目,个人小团队的在线文档“简单、易用、轻量化”还专门针对API文档、技术文档做了优化
前端·程序员·github
小青鱼6 天前
AI编程-Cursor从入门到精通系列之常用概念及解释(二)
人工智能·程序员
捡田螺的小男孩6 天前
参数校验的十个建议!收藏好,别再给测试机会提bug~
java·后端·程序员