用 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 就好了。
相关推荐
我是陈泽19 小时前
一行 Python 代码能实现什么丧心病狂的功能?圣诞树源代码
开发语言·python·程序员·编程·python教程·python学习·python教学
肖哥弹架构2 天前
Spring 全家桶使用教程
java·后端·程序员
IT杨秀才5 天前
自己动手写了一个协程池
后端·程序员·go
程序员麻辣烫7 天前
像AI一样思考
程序员
一颗苹果OMG8 天前
关于进游戏公司实习的第一周
前端·程序员
万少8 天前
你会了吗 HarmonyOS Next 项目级别的注释规范
前端·程序员·harmonyos
楽码9 天前
彻底理解时间?在编程中使用原子钟
后端·算法·程序员
江南一点雨10 天前
又一家培训机构即将倒闭!打工人讨薪无果,想报名的小伙伴擦亮眼睛~
java·程序员
用户861782773651810 天前
ELK 搭建 & 日志集成
java·后端·程序员
河北小田10 天前
局部变量成员变量、引用类型、this、static
java·后端·程序员