如何开发一个 Hugo 主题组件

本文将通过开发一个 caniuse shortcode 来演示如何创建一个 Hugo 主题组件。

简介

本文旨在阐述从 0 到 1 开发一个组件的过程,而不会过度解读组件的概念和原理。关于 Hugo 主题组件的更多信息,请参阅 贡献指南 / 开发组件 页面。

本文案例开源在 GitHub hugo-fixit 组织下。

一个含有 caniuse shortcode 的 Hugo 主题组件。

HTML 2

创建组件骨架

创建组件和创建主题是一样的,可以通过 hugo new theme 命令来创建一个新的主题组件。

1
hugo new theme shortcode-caniuse

上述命令会在 themes 目录创建一个 shortcode-caniuse 的文件夹,它是一个完整的 Hugo 主题目录结构。

由于我们只需要开发一个包含 shortcode 的组件,所以可以删除不需要的文件,删除后的目录结构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
./
├── assets/
├── i18n/
├── layouts/
│   ├── partials/
│   └── shortcodes/
├── hugo.toml
├── LICENSE
├── README.md
└── theme.toml

为了之后能同时兼容 Git submodule 和 Hugo Modules 的安装方式,还需初始化 Git 仓库和 go.mod 文件:

1
2
3
git init
git remote add origin git@github.com:hugo-fixit/shortcode-caniuse.git
go mod init github.com/hugo-fixit/shortcode-caniuse

创建 Shortcode

layouts/shortcodes 目录下创建一个名为 caniuse.html 的文件。

根据 The CanIUse Embed 的使用说明,编写 shortcode 内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{{- /* 
  reference https://github.com/pengzhanbo/caniuse-embed
  <feature>: Feature name
  <past>: Show the past N versions that match the feature, range is 0 - 5, default is 2
  <future>: Show the future N versions that match the feature, range is 0 - 3, default is 1
*/ -}}
{{- $feature := cond .IsNamedParams (.Get "feature") (.Get 0) -}}
{{- $past := cond .IsNamedParams (.Get "past") (.Get 1) | default 2 -}}
{{- $future := cond .IsNamedParams (.Get "future") (.Get 2) | default 1 -}}

<p class="ciu-embed" data-feature="{{ $feature }}" data-past="{{ $past }}" data-future="{{ $future }}" data-observer="true" data-theme=""></p>
{{- /* EOF */ -}}

创建 JS 文件

为了在 FixIt 主题切换黑白主题色的同时,shortcode 也跟着改变主题色,我们可以通过 JS 处理。

assets/js 目录下创建一个名为 shortcode-caniuse.js 的文件,在里面写入切换主题逻辑处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function setCanIUseEmbedsTheme(allCanIUseEmbeds, isDark) {
  allCanIUseEmbeds.forEach(function (embed) {
    embed.setAttribute('data-theme', isDark ? 'dark' : 'light');
  });
}

function CanIUseShortcodeInit() {
  if (typeof window.fixit?.switchThemeEventSet === 'object') {
    const allCanIUseEmbeds = document.querySelectorAll('.ciu-embed');
    setCanIUseEmbedsTheme(allCanIUseEmbeds, window.fixit.isDark);
    window.fixit?.switchThemeEventSet.add(function (isDark) {
      setCanIUseEmbedsTheme(allCanIUseEmbeds, isDark);
    })
    return;
  }
}


if (document.readyState !== 'loading') {
  CanIUseShortcodeInit();
} else {
  document.addEventListener('DOMContentLoaded', caniuseShortcodeInit, false);
}

创建 Partial

layouts/partials/inject 目录下创建一个名为 shortcode-caniuse.html 的文件。

引用第三方插件和组件本身的 JS 资源,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{{- if .HasShortcode "caniuse" -}}
  {{- $fingerprint := .Scratch.Get "fingerprint" -}}

  {{- /* embed.js */ -}}
  {{- $source := resources.Get "lib/shortcode-caniuse/embed.js" -}}
  {{- if hugo.IsProduction | and .Site.Params.cdn -}}
    {{- $source = "https://caniuse.pengzhanbo.cn/embed.js" -}}
  {{- end -}}
  {{- dict "Source" $source "Fingerprint" $fingerprint "Attr" `type="module"` "Defer" true | dict "Scratch" .Scratch "Data" | partial "scratch/script.html" -}}

  {{- /* shortcode-caniuse.min.js */ -}}
  {{- $options := dict "targetPath" "js/shortcode-caniuse.min.js" "minify" hugo.IsProduction -}}
  {{- if not hugo.IsProduction -}}
    {{- $options = dict "sourceMap" "inline" | merge $options -}}
  {{- end -}}
  {{- dict "Source" (resources.Get "js/shortcode-caniuse.js") "Build" $options "Fingerprint" $fingerprint "Defer" true | dict "Scratch" .Scratch "Data" | partial "scratch/script.html" -}}
{{- end -}}

发布组件

发布之前,更新一下 LICENSEREADME.mdtheme.toml 文件,然后提交到远程仓库即可。

如何使用

安装组件

安装主题 一样,安装方式有多种,选择其一即可,例如通过 Hugo Modules 安装:

1
2
3
4
5
[module]
  [[module.imports]]
    path = "github.com/hugo-fixit/FixIt"
+ [[module.imports]]
+   path = "github.com/hugo-fixit/shortcode-caniuse"

注入 Partial

FixIt 0.3.9 | 新增

通过 FixIt 主题开放的自定义块,在 layouts/partials/custom.html 文件将 shortcode-caniuse.html 注入到 custom-assets 中:

1
2
3
{{- define "custom-assets" -}}
  {{- partial "inject/shortcode-caniuse.html" . -}}
{{- end -}}

使用 Shortcode

caniuse shortcode 有以下命名参数:

  • feature [必需]第一个位置参数)特性名称
  • past [可选]第二个位置参数)显示过去 N 个版本,范围是 0 - 5,默认为 2
  • future [可选]第三个位置参数)显示未来 N 个版本,范围是 0 - 3,默认为 1

点击 caniuse.com 网站上功能左边 # 号,URL 中的 pathname 即为 feature 参数。

这是一个用法示例:

1
2
3
{{< caniuse feature="css-grid" >}}
或者
{{< caniuse "css-grid" >}}

呈现效果如下:

参考

致谢


相关内容

0%