本文将通过开发一个 caniuse shortcode 来演示如何创建一个 Hugo 主题组件。
简介 
本文旨在阐述从 0 到 1 开发一个组件的过程,而不会过度解读组件的概念和原理。关于 Hugo 主题组件的更多信息,请参阅 贡献指南 / 开发组件  页面。
本文案例开源在 GitHub hugo-fixit  组织下。
一个含有 caniuse shortcode 的 Hugo 主题组件。
HTML 
创建组件骨架 
创建组件和创建主题是一样的,可以通过 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   -}} 
发布组件 
发布之前,更新一下 LICENSE、README.md 和 theme.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 主题开放的自定义块,在 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,默认为 2future  [可选] (第三个 位置参数)显示未来 N 个版本,范围是 0 - 3,默认为 1点击 caniuse.com 网站上功能左边 # 号,URL 中的 pathname 即为 feature 参数。
这是一个用法示例:
1
 2
 3
 {{<  caniuse  feature = "css-grid"  > }}
 或者
 {{<  caniuse  " css-grid "  > }} 
呈现效果如下:
参考 
致谢