博客从 org-static-blog 迁移 Hugo 小记


Published: 2021-07-10, Last Updated: 2025-02-15

我从 2018 年开始用 org-static-blog 写博客到现在也有几年时间了,写的文章比较少,总体上这个简单的静态网站生成方案都能够满足需求。

但中途也发现一些不够用的地方,比如之前想要并贡献了一个生成 meta description 的 PR ,以便更好地在搜索引擎展示结果( SEO );又比如中途一度有区分中英双语博客的需求( index 列表、 RSS 等),但苦于没有想到很好的方法,就只能把两种文章都混杂在一起展示,但这样用户的阅读体验不好;另外之前的样式也差强人意,比如主页的链接带下划线,不利于阅读。

/img/2021-07-10-org-static-blog-homepage.jpg
旧主页样式
/img/2021-07-10-org-static-blog-post.jpg
旧博文样式

最近又有了一些其他的想法,就考虑做一个升级方案,它需要能够满足以下需求:

  1. 支持 org-mode 标记语法
  2. 支持中英双语
  3. 较完善的 SEO 支持
  4. 较完善的社交分享功能
  5. 能集中展示个人的其他社交网站链接
  6. 除了博客之外,还能够展示其他的内容
  7. 界面稍微美观一点

进一步细化需求,需要考虑的因素有:

- [X] 保持 blog, tags, rss 链接不变
  最重要的是已有的博文 url 不能变。但在这个过程中我发现这件事还没那么简单,配置起来很麻烦;最终决定 url 变了就变了吧,把原来的博客 html 内容改为 meta refresh 来重定向到新博文 url ,这样比较简单通用。
- [X] SEO 功能验证
  - [X] robots.txt
  - [X] sitemap
  - [X] 支持 description ( front matter 已内置支持)
- [X] front matter 支持文章更新时间
  lastmod 字段
- [X] 社交分享效果验证
- [X] 选择一个合适的主题 theme
  需要支持的特性( 综合考虑,最终选择 beautifulhugo ):
  - [X] 需要 responsive
  - [X] 多语言支持
  - [X] 展示 social links
  - [X] 社交分享支持
  - [X] 首页文章倒序,最新的在最上面
  - [X] 文章内部如何支持显示更新时间、其他语言版本?
    不支持
  - [X] 支持 toc 目录
    暂不支持,但可以接受: https://github.com/halogenica/beautifulhugo/issues/219
  - [X] 验证分页条数及效果
    默认 10 条

根据 org-mode 官网 Blogs and Wikis with Org - orgmode.org 推荐,大致考察了几种方案:

  • Jekyll 不支持 org-mode ,仅支持 Markdown ;采用 Ruby 编写的,不太熟悉;
  • Hexo 不支持 org-mode ;采用 Node.js 编写,庞大的依赖让人望而却步;
  • Hugo 支持 org-mode ,采用 Golang 编写,只有一个二进制文件,无依赖;另外它编译 org-mode 文件到 HTML 的速度极快(后面会提到);

最终决定采用 Hugo ,我本以为很快能搞定,结果没想到正确配置 Hugo 并没有那么简单。

迁移过程及遇到的问题

第一次使用没有经验,需要先建一个 hello world 工程来玩玩。

按照官方文档 Quick Start | Hugo - gohugo.io ,先 hugo new site hello-world 创建一个项目,然后修改主题 theme = "ananke" ,并新建一个 post 然后 hugo server -D 启动开发版 web server 。

到目前为止,整个过程都很顺滑,尤其是它的编译速度非常快,只要 100+ms ,这着实惊艳到我了!🤯而且 Hugo 支持实时加载(live reload),就是当你更新并保存了你的 org-mode 文件之后, Hugo 会自动重新编译并触发浏览器刷新当前页面,减少了人工操作,写作体验超级丝滑!

/img/2021-07-10-hugo-fast-build.jpg
编译只要 121ms ,非常快!

用 Git 保存好当前的 config.toml ,继续尝试。

然后按照文档指引,根据多语言的设置配置,结果发现 ananke 并不能很好地支持多语言(如果没记错的话,它不支持目录结构的多语言模式),于是不得不换别的主题尝试。

结果发现换一个主题之后,原来 ananke 主题下的 config.toml 在新主题下却不能工作了。

回顾整个过程,遇到的主要问题主要有2个:

  1. 多语言目录结构是怎样的?
  2. 如何调整一个主题的设置?

一开始以为 Hugo 主题只负责 UI 的渲染,与 Hugo 核心没有太多耦合的东西,一个 config.toml 就能够轻松适配多种不同的主题。结果在更换主题的过程中发现并不是这样的,而且每一种主题一般都有一个样例配置 exampleSite/config.toml ,这才渐渐明白 Hugo 的配置其实是 以主题配置为中心 ,从主题附带的示例配置出发进行微调,整个过程会顺利很多。

多语言的目录结构

接下来说说多语言的目录结构问题。

关于多语言的设置,文档有一页 Multilingual Mode | Hugo - gohugo.io 专门细说。主要有两种方式:

  1. 同一个文件夹下,以不同的文件名“后缀”表示比如的语言,比如 hello-hugo.en.md hello-hugo.zh-cn.md 分别表示同一篇文章的英文和中文版本。
  2. 通过不同文件夹来区分,比如一个 en 文件夹专门放英文文章,另外一个 zh-cn 专门放中文文章。 官方文档给出的示例是这样的:

    /content/english/about.md
    /content/french/about.md

我比较倾向于第二种方式,这样文件归类比较清晰明了。

但是,事与愿违,偏偏在选主题的过程中发现很多主题都只能支持第一种方式,不支持第二种。

而且要命的是,对于第二种方式的目录结构,文档也没有明确的说明,导致我一直没有弄明白正确的目录结构。

比如,我这样设置中英文博文的内容目录 contentDir :

[languages]
  [languages.en]
    contentDir = "content/en"
    languageName = "English"
    weight = 1
  [languages.zh-cn]
    contentDir = "content/zh-cn"
    languageName = "简体中文"
    weight = 2

最初我以为配置要求的目录结构是这样的:

content/
  posts/
    en/
    zh/

但通过摸索实践,最终发现它其实要求这样的:

content/
  en/
    posts/
  zh/
    posts/

这里发现的过程还挺有意思的,算是一种探索、发现新知识的手段。在经历了各种搜索都找不到结果、官方文档也很模糊的情况下,如何得知它的正确目录结构这个知识呢?——“大胆尝试,小心验证。”

要不试试新建一篇文章,看它放在哪里了?

$ hugo new posts/test.org    
/path/to/hello-world/content/posts/en/posts/test.org created

通过琢磨这里新建文件的路径,我终于明白了多语言的目录结构。如上边所说,其实就是每种语言目录都是一个新的根目录,当时还特意发了一条推文宣布这个发现:

TOML 文件格式

TOML 现在的全称叫 Tom's Obvious, Minimal Language (这个 Minimal 挺难评的),以前叫 Tom's Own Markup Language 。由 Tom Preston-Werner 创建,他是 GitHub 的联合创始人,也是个开发者。

TOML 语法及 Hugo 配置语法实在不怎么好用,故这里对我比较困惑的地方做个简单的了解和记录。

  • TOML 语法本身区分大小写,但是 Hugo 配置似乎不怎么区分。比如我测试发现 Params vs params 都可以。
  • 开头的缩进不起没有作用,所以才会有 [languages.en] 这样的写法用于表示配置结构上的缩进。
  • Array of Tables 用 Clojure 的话来讲就是一个 map 数组。

详细的语法规格见 TOML spec

最终效果

经过对原来 org-static-blog 的 org-mode 源文件进行了一些微整,以及对 beautifulhugo 主题的修饰之后,终于生成了现在这个比较满意的效果。

对于 org-mode 源文件的调整,有以下微调:

  • date tags 这两个 front matter 格式调整
  • 图片路径更改
  • 博文的 URL 调整

    由于博客访问量并不大,我可以接受 URL 变更,但原 url 还是保留,毕竟 404 对于读者太不友好了。具体做法就是,原博文的 html 通过 meta tag 来实现类似的 301 重定向,比如这样:

    <!DOCTYPE html>
    <html>
      <head>
        <meta http-equiv="refresh" content="0; url=/zh-cn/blog/2021-02-27-wrk-yuanli/">
      </head>
      <body>
        <p>The page has moved to:
          <a href="/zh-cn/blog/2021-02-27-wrk-yuanli/">here</a></p>
      </body>
    </html>

对于 beautifulhugo 主题,主要参考 Keep Coding - liujiacai.net 的网站进行了配色调整,更加便于阅读。(具体做法是,拷贝一份 beatifulhugo 仓库中的 main.css 到自己工程 static/css/main.css ,然后再进行修改微调。)

遗留问题

虽然现在网站结果总体比较满意,但还存在一些小问题:

  1. 每一级标题无法使用编号,不利于阅读
  2. beautifulhugo 页面性能较差,打开一片文章会发起 30+ 个 HTTP 请求

    /img/2021-07-10-beautifulhugo-performance.jpg
    Lighthouse 测评结果表明性能较慢
  3. 本地预览版本也会启用 Google Analytics ,污染数据

    有两种解决方法:

    1. 维护两份配置,本地预览和部署版本通过 --config <xxx.toml> 来区分,也可以同时传入多个配置文件,通过 , 逗号隔开
    2. 以目录组织配置文件,通过命令行参数 --production 来指定环境;环境名就是 config/ 内的子目录名, _default/ 目录放兜底的全量配置。

      比如这样的目录结构:

      $ tree config/
      config/
      ├── _default
      │   └── config.toml   # 全量配置,不配置 GA id
      └── production
          └── config.toml   # 配置 GA id
      
      2 directories, 2 files

    第二种方法比较符合我的胃口,配置结构清晰。有了上面的配置结构,通过 hugo server -D 启动的本地版本就不再上报 GA ;修改博客达到满意的效果之后,再 hugo --environment production 生成包含 GA 代码的博客全部 html 文件。

    参考:

  4. footer 中的版权年份展示不对,按道理应该要展示到 2021 年 (突然又好了,不知道为何) 文档说明在 https://gohugo.io/variables/site/

Hugo 使用感受

Hugo 优点:很快,基本能满足我的大部分需求。

不足之处:

  1. 文档看起来很全,但是却很难快速地找到有效信息; 比如:

    • 多语言的目录结构
    • 配置文件参数有点乱,比如 [params] vs. [Params] 齐飞(第一个字母大小写无关?),比如 [Params] 参数存在的意义(或者说为何要定义这么一节配置)?
  2. 配置 config.toml 与主题强相关, 换一个主题基本要重新配置 ,这点在文档中也没提到; 根据这几天的经验,我的最佳实践是,从选定主题的仓库中 copy 一个初始的 exampleSite 配置,然后再慢慢微调,这样会比较省力😼。
  3. 有的 org-mode 特性 Hugo 不支持,比如:

    1. include 指令
    2. 不支持 Evaluating Code Blocks

总体而言,如果你也是 Emacs 和 org-mode 用户,那么 Hugo 来写博客还是不错的,可以入坑👍。


也可以看看

comments powered by Disqus