Hugo: a static-site generator

Ben Hoyt

Hugo:静态网站生成器

原文由 Ben Hoyt 发布,订阅该博客

静态网站生成器将用标记语言编写的页面内容渲染成完整的 HTML,方便开发者上传生成结果,以简单、安全的方式提供网站服务。本文将介绍 Hugo,这是一款用 Go 编写、针对速度进行优化的静态网站生成器。它是一个灵活的工具,可配置以适用于多种场景:简单的博客、项目文档、大型新闻网站,甚至政府服务网站。

背景

静态网站生成器或许自 HTML 诞生之初就已存在:大多数网站都有通用的页眉和页脚,网站所有者不想在每个页面中手动复制粘贴这些内容。那时,开发者更可能写上几十行自定义的 Perl 或 Bash 脚本来拼接 HTML。这种做法确实可行,但如今的静态网站生成器比简单的自定义脚本更快、更强大。

大量网站通过为每次页面访问动态渲染内容来处理通用页眉(及其他类似部分),例如使用连接数据库的 PHP。到了 2020 年,开发者可能更倾向于使用连接 HTTP API 以提供内容的 React 前端。然而,当网站只需要提供基本静态的内容时,这两种方案都显得有些过度设计。

许多个人网站和博客,以及项目和公司的文档网站,都可以采用静态方式生成。提供预渲染的 HTML 比提供动态渲染的内容更快、更可靠。维基百科列出了静态网站的一些优势

  • 相比动态网站安全性更高(动态网站在存在漏洞时容易受到网页后门攻击)
  • 相比动态网站,为最终用户提供更好的性能
  • 对数据库或其他应用服务器等系统的依赖更少,甚至完全没有依赖

当然,有些网站(尤其是网络应用)始终需要动态内容。即使是 LWN,其文章内容本身是静态的,也需要能够为已登录的订阅者和未登录的访客提供不同的内容。因此,对动态服务器和 API 的需求始终存在。但当网站内容是静态且公开的,或者任何动态内容都可以在客户端处理时,静态网站生成器就能显著简化整个系统。

由于静态网站生成器相对容易构建,因此有许多开源选择。几乎所有主流编程语言都有相当流行的方案,例如:Ruby 的 Jekyll、Python 的 Pelican、Java 的 JBake、PHP 的 Sculpin、JavaScript 的 Hexo,以及其他数百种。本文将关注 Hugo,这是一个活跃且维护良好的开源项目。

Hugo 由 Steve Francia(现为 Go 核心团队成员)于 2013 年创建,但自 2015 年起由 Bjørn Erik Pedersen 接任主要开发者。该项目托管在 GitHub 上,目前包含约 43,000 行 Go 源码(不含注释),采用宽松的 Apache License 2.0 许可证。它拥有一个详尽的文档网站,当然也是用 Hugo 构建的。该项目还有广泛的社区参与,拥有一个相当活跃的讨论论坛和大量用户提交的主题。Pedersen 在 2017 年表示,Hugo 的基础设施(而非开发)得到了一些企业赞助,"Netlify 免费托管我们的网站,Discourse 维持论坛运行。Travis、Appveyor 和 CircleCI 负责构建。除此之外,没有其他赞助商。"

网站生成器是用什么编程语言编写的其实并不太重要(我个人使用 Jekyll,但从未需要编写 Ruby),尽管开发者往往更倾向于选择用自己熟悉的语言编写的工具。即使使用 Hugo,开发者也可能永远不需要编写 Go 代码,不过如果要构建或定制主题,就可能需要接触 Go 的 HTML 模板语法

据 BuiltWith 统计,过去三年中 Hugo 的使用量急剧增长。它被多个知名项目所采用,包括重新设计的 Smashing Magazine 网站1Password 支持网站博客Let's Encrypt 网站,以及美国政府的 Digital.gov 网站

工作原理

在不带任何选项运行的情况下,hugo 会遍历 Markdown 文件所在的 content 子目录,并将渲染后的 HTML 输出到名为 public 的子目录中。所有这些都是可配置的——默认配置文件是项目根目录下的 config.toml。输出会根据主题进行渲染,主题由少量 Go HTML 模板组成,用于精确定义生成什么样的 HTML。主题还包含 CSS 样式以及主题所需的任何图片或 JavaScript 文件。

在撰写本文之前,我搭建了一个包含三篇文章和一个“关于”页面的简单测试网站——这正是个人网站或博客常见的类型。选择主题、配置 Hugo 并添加一些示例内容大约花了 30 分钟。Hugo 可以安装自主流操作系统的包管理器、GitHub 上预编译的二进制文件(支持 Linux、各种 BSD、macOS 和 Windows),或从源码构建。对于 Hugo 来说,从源码构建非常简单,只需安装 Go(如果尚未安装)、克隆项目,然后执行 go build 即可。在我的机器上,从克隆到运行“hugo version”大约只用了 20 秒。

在开发过程中,输入“hugo server”会启动一个本地网络服务器,从内存中提供渲染后的内容。在这种模式下,Hugo 会自动监视文件并根据需要重新构建 HTML。渲染后的页面中甚至包含一些 JavaScript,能够在内容发生变化时自动在浏览器中重新加载页面(通过 WebSocket 使用 LiveReload 实现)。Hugo 内置的服务器基于可用于生产环境的 net/http 服务器,但在部署时,通常更简单的做法是预先构建好渲染文件并上传到 Amazon S3 等静态网站托管服务,或通过 NGINX 等常规网络服务器提供服务。

与其他静态网站生成器一样,Hugo 为页面使用一种简单的文件格式,由 YAML 格式的“front matter”组成,通过“---”行与 Markdown 内容分隔开。以下是我示例网站中第一篇文章的内容:

---
title: "First Post"
date: 2020-07-06T09:33:48+12:00
categories:
- Development
---

The quick brown fox jumps over the lazy dog. Ee equals em cee squared.
Let's try a [link](https://benhoyt.com/) and **some bold text**.

Another paragraph. Hugo seems to be working. Writing Markdown is nice.

为了让人了解 Hugo 主题模板是什么样子,下面是我在测试中使用的 Soho 主题中的 single.html 模板(它用于渲染单篇博客文章):

{{ define "main" -}}
<div class="post">
  <h1>{{ .Title }}</h1>

  {{ if ne .Type "page" -}}
  <div class="post-date">
    <time datetime="{{ .Date.Format "2006-01-02T15:04:05Z0700" }}">
        {{ .Date.Format "Jan 2, 2006" }}
    </time>
    &middot; {{ .ReadingTime }} min read
  </div>
  {{- end }}

  {{ .Content }}
</div>
{{- end }}

Hugo 支持“taxonomies”(分类法),即对页面进行分类的不同方式,并且会为每种分类法创建一个页面,列出所有归属于该分类的文章。默认情况下,它会创建“tags”(标签)和“categories”(分类)两种分类法,例如我测试网站上的“Development”分类。文档中展示了对分类进行排序或为其添加元数据的各种方法。

该工具支持多种方式来控制渲染内容中的页面 URL(“permalinks”,即固定链接),默认的 /:section/:filename/ 会生成类似 /posts/third-article/ 的 URL。其中 :section 名称来自 content 中的子目录名(例如 posts),而 :filename 则是内容来源文件去掉扩展名后的文件名。这些“目录 URL”是通过在目录中创建一个带有索引文件的已渲染 HTML 文件来实现的:/posts/third-article/index.html

Hugo 的一项更高级的功能是它所称的“shortcodes”(短代码),即带有参数的 HTML 小片段,可用于扩展 Markdown 有限的功能,而无需回退到直接编写 HTML。内置的短代码有很多,例如 gist(用于嵌入 GitHub Gist)、highlight(语法高亮代码)和 youtube(嵌入式 YouTube 视频)。例如,以下短代码:

{{< youtube w7Ft2ymGmfc >}}

将生成如下 HTML 输出:

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/w7Ft2ymGmfc?autoplay=1"
      style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
      allowfullscreen title="YouTube Video"></iframe>
</div>

Hugo 还包含更多高级功能,例如根据文章中的标题生成目录、支持替代的标记格式(默认为 Markdown)、用于预处理 CSS 和 JavaScript 的管道,以及用于调整和裁剪图片的图像处理命令。

Hugo 以速度快著称,其官网称“按每页不到 1 毫秒计算,平均网站构建时间不到一秒”。据称 Smashing Magazine 拥有 7500 个页面的网站构建时间约为 13 秒。这种速度部分归因于它是用 Go 编写的,Go 是一门可编译为原生二进制文件的语言(而非像 Python 那样基于字节码解释的语言),同时也得益于贡献者多年来在基准测试和性能方面所做的各种努力;例如,可参阅 2020 年 1 月发布的 0.63 版本的发布说明

一般来说,使用静态网站生成器时,内容作者需要是开发者,或至少熟悉文本编辑器、Markdown 和 Git 的使用。不过,网站托管商 Netlify 提供了一款名为 Netlify CMS 的开源工具,允许非开发者使用所见即所得编辑器进行创作——该工具会在后台自动提交到 Git 仓库。Hugo 的文档中有一份清单,列出了其他类似工具。

托管方案

通过静态网站生成,开发者可以选择将渲染后的成果托管在任何主机或网络服务器上。流行的选择包括 GitHub Pages、Amazon S3、Netlify,或使用 Caddy 或 NGINX 网络服务器自行托管。Hugo 有一个文档页面,提供了各种方案的详细配置信息。

对于我的测试网站,我决定使用 Amazon S3(前端搭配 AWS 的 CloudFront CDN)。这种方式设置起来相当简单,每月仅需几美元就能应对数百万次页面访问。当然,你也可以完全不使用云服务,而使用手头现有的任何服务器进行托管——即使是廉价服务器,每秒也能处理数千个静态文件请求。

使用持续集成(例如 GitHub Actions)在每次向网站 Git 仓库提交时运行 Hugo 构建也非常简单,这样可以确保渲染后的版本始终保持最新。

总结

Hugo 似乎没有公开的路线图或固定的发布计划;在过去几年中,大约每月发布一次,期间还会穿插错误修复版本。2018 年 7 月,Pedersen 谈及“通往 1.0 之路”,但目前维护者似乎对 0.x 的版本号相当满意。版本的命名往往轻松有趣,例如“圣诞版”和“40K GitHub Stars 纪念版”。

静态网站生成器是构建和部署以静态内容为主的网站的相对简单的方式。Hugo 的速度、宽松的许可证以及丰富的功能,使其成为开发者的一个有吸引力的选择,无论是用于小型个人网站还是大型内容密集型网站。总的来说,Hugo 是一个值得关注的活跃项目。

[感谢提出这一选题的 Jim Garrison。]

本文章由 muse-spark-1.2-contributor 进行翻译

评论