Hugo添加自定义css和javascript

最近从Hexo切到了Hugo博客生成引擎,主要原因是Hexo太慢,且在页面数量上千时会崩溃

# Who Should Use Hugo?

Hugo is for people building a blog, a company site, a portfolio site, documentation, a single landing page, or a website with thousands of pages.

使用任何引擎,必须要解决添加自定义css和javascript的问题,才能非常方便地定制化。

可惜Hugo对于初学者不太友好,研究添加css和js费了不少功夫,需要通过读文档理解Hugo的设计思路。相比而言,Hexo的插件系统Hexo injector对新手更友好和容易理解。所以我们聊聊在Hugo中添加自定义css和js的正确方式。

不关心原理的急性子可以直接跳到修改模板代码节进行实操 :-)

Hexo添加自定义css/js

在Hexo中添加自定义js非常简单,只要在根目录的scripts目录增加一个新文件custom.js,这段js就会被自动添加到<head>的末尾(css类似):

hexo.extend.injector.register('head_end', () => {
  return '<script data-ad-client="ca-pub-xxx" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>';
}, 'default');

Hugo添加自定义css/js的正确方式

所谓正确方式,类似Python中的pythonic。在Hexo中添加css/js的Hexoic方案是injector。那么Hugoic的方案是什么?

Hugo依赖主题模板运行,添加自定义css,需要修改所使用的主题的模板文件。而改它们之前,必须对Hugo中的很多概念有基本认识。

Hugo模板查找顺序

首先理解Hugo的模板查找顺序:# Hugo Layouts Lookup Rules With Theme:

In Hugo, layouts can live in either the project’s or the themes' layout folders, and the most specific layout will be chosen. Hugo will interleave the lookups listed below, finding the most specific one either in the project or themes.

可惜这里没有明确表示project和theme layout哪个的优先级更高。参考 # Customizing a Theme

定制模板方法

基于Hugo模板查找顺序,得出定制模板步骤:

  • 拷贝/themes/<yourtheme>/layouts/partials/xxx.html到项目根目录/layouts/partials/xxx.html,即在项目根目录中新建与主题一样的同构目录,其中xxx.html是你要定制的模板文件
  • 修改/layouts/partials/xxx.html,添加插入css/js的代码段

这样设计的好处在于主题repo作为submodule可以独立于项目repo升级,如果直接修改主题文件并提交,在升级主题时就会有诸多冲突,管理不便。

初次看到这种做法时很费解,到底是在project layout还是theme layout新建模板文件xxx.html?为什么可以这么做?耐着性子读了不少文档后才明白,Hugo有不少隐式的规则,这里原因在于project layout文件夹中的模板文件会覆盖theme layout的模板

layouts/_default/中是一些主页、列表之类的默认模板,layouts/partials中是一些更小的模板块:

Partials are smaller, context-aware components in your list and page templates that can be used economically to keep your templating DRY.

Partial模板的 # 查找顺序

However, partials are simpler in that Hugo will only check in two places:

  • layouts/partials/*<PARTIALNAME>.html
  • themes/<THEME>/layouts/partials/*<PARTIALNAME>.html

This allows a theme’s end user to copy a partial’s contents into a file of the same name for further customization.

修改模板代码

终于可以开始操刀,这里以PaperMod模板为例,拷贝/themes/PaperMod/layouts/partials/head.html/layouts/partials/head.html,并在文件末尾添加如下代码:

...
{{ range .Site.Params.customcss }}
    <link rel="stylesheet" href="{{ . | absURL }}">
{{- end }}

{{ range .Site.Params.customjs }}
    <script type="text/javascript" src="{{ . | absURL }}"></script>
{{- end }}

{{ range $elem := .Site.Params.customjscssraw }}
    {{ $elem | safeHTML }}
{{- end }}

看懂这段代码需要理解 # Context 的概念,和几个函数:rangeabsURLsafeHTML# Introduction to Hugo Templating 中有详细介绍,不再赘述。

为什么有了customcsscustomjs还要再加customjscssraw的支持?原因在于有时添加原始html更方便,比如css带有integrity属性,或js需要async关键字等等。customjscssraw最为灵活,与Hexo injector可以达到一样的效果。可参考下节中的示例。

使用方法

使用很简单,在项目目录中的config.yml中按需添加如下字段即可(此处覆盖了多种应用场景,包括多行js的情况):

params:
  ...
  customcss:
    - "css/xx.css"
  customjs:
    - "js/xx.js"
  customjscssraw:
    - "<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/all.min.css' integrity='sha256-2H3fkXt6FEmrReK448mDVGKb3WW2ZZw35gI7vqHOE4Y=' crossorigin='anonymous'>"
    - >
      <script async src='https://www.googletagmanager.com/gtag/js?id=G-XXX'></script>
      <script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());

        gtag('config', 'G-XXX');
      </script>

Tips: 如果插入的css/js是相对路径,别忘了在项目的static/目录中添加对应的静态文件。

参考: # Add Custom CSS Or Javascript To Your Hugo Site