Back
Featured image of post 如何优雅的从 Hexo 转移 Blog 到 Hugo

如何优雅的从 Hexo 转移 Blog 到 Hugo

详细记录了本 Blog 从 Hexo 迁移至 Hugo 的过程。

为什么使用 Hugo / 我们使用的主题

认识 Hugo

  • Hugo 是使用 Go 语言编写的高性能 Blog / 站点 框架,在全球拥有广泛的用户群和数以百计优质主题。
Hugo Logo

安装

常规安装方法

  • macOS
brew install hugo
  • Windows
$ choco install hugo -confirm
  • Linux
$ snap install hugo
  • 通过官方源安装,但这将会导致 PandocMathJax 的使用体验直线下降。
  • 通常而已你会遇到这些问题:
    • 如果使用默认的 Goldmark 渲染器选项
      • 原有 Hexo Blog 上的公式均需要以四斜杠换行,即: \\\\ \(\to\) \\\\\\\,这将进一步导致文章难以被其他 Markdown 引擎渲染(比如你需要使用 Visual Studio Code 协同编辑你的 Blog)
      • 一些公式将会因为 Goldmark 极其魔幻的匹配规则被”消失“,或错位
      • 一些图片将会因为 Goldmark 极不具宽容度的 Markdown 语法匹配(尽管可以关闭严格模式)
    • 如果你尝试使用官方支持的 Pandoc 渲染器选项
      • 这将会导致文章 TOC(目录)消失,
      • 主题的 Markup Hook 无法使用,文章内的所有 HTML 内容将为 Pandoc 直出(即无任何 CSS, Class 标签等)

主题的 Markup Hook 为 Hugo v0.71.0 的新特性,目前仅支持 Goldmark 渲染器

从源编译安装

  • 因为 Hugo 几乎写死了所有渲染器的调用参数(这点真的是 Hugo 相较于 Hexo 很不成熟的一个体现),而 Pandoc 渲染器需要在调用参数添加 --toc 选项为文章自动输出目录,所以我们必须魔改 Hugo 的部分代码。

  • 更重要的是,Hugo 主题使用的 TOC 显示一般是通过 Hugo 内部传参,单纯的给 Pandoc 添加 --toc 参数会导致所有文章只是在输出头部,而不是以主题在侧边栏的形式,如下图所示。

From olOwOlo/hugo-theme-even Issue
Pull Request: Add basic toc generation for pandoc #8911
  • 我们为了保证 Hugo 在最新版本,将 #8911 的更新合并到了最近的 Hugo 最新版本。
    • 你可以在 SDLMoe/hugo 找到我们已经合并过后的版本,但请注意,我们并不保证能实时更新 Hugo 至最新版本。

迁移 Hexo 文章至 Hugo

  • 首先,使用 hugo new site <site-name> 创建站点,此命令会新建一个名为 site-name 的文件夹,类似于在 site-name 文件夹里面输入 hexo init

  • 生成结果如下

.
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── static
└── themes
  • 相较于 Hexo,Hugo 的站点文件夹非常干净没有恶心人的 node_modules,配置文件集中在 config.toml,当然你可以直接改名为更为习惯的 config.yaml

配置文件

  • 以下为本站的配置文件,仅作参考,隐去了部分信息
baseurl: https://sdl.moe 
languageCode: zh-cn
theme: hugo-theme-stack
paginate: 5 # 每页显示多少文章
title: SDLMoe

# Set hasCJKLanguage to true if DefaultContentLanguage is in [zh-cn ja ko]
# This will make .Summary and .WordCount behave correctly for CJK languages.
hasCJKLanguage: true

permalinks:
    post: /post/:filename/
    page: /:filename/

# 下面是 Stack 主题的参数

params:
    mainSections:
        - post
    featuredImageField: image
    rssFullContent: true # RSS 是否开启全内容 / false 则为只有摘要
    favicon: img/sdl-logo-white-cir.webp # 浏览器 Tab 页的网站图标

    article:
        math: false # 这里关闭是因为我们使用的是 MathJax
        toc: true # TOC 默认开启
        readingTime: true
        license:
            enabled: true
            default: '<a href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" rel="nofollow noopener">CC BY-SA 4.0</a>'

    imageProcessing:
        cover:
            enabled: true
        content:
            enabled: true

### Custom menu
### See https://docs.stack.jimmycai.com/configuration/custom-menu.html
### To remove about, archive and search page menu item, remove `menu` field from their FrontMatter
menu:
    main:
        - identifier: home
          name: Home
          url: /
          weight: -100
          params:
              newTab: false
              icon: home

        - identifier: categories
          name: Categories
          url: /categories
          weight: -85
          params:
              newTab: false
              icon: layout-grid

        - identifier: rss
          name: RSS Feed
          url: /index.xml
          weight: -10
          params:
              newTab: true
              icon: rss

    social:
        - identifier: github
          name: GitHub
          url: https://github.com/SDLMoe
          params:
              newTab: true
              icon: brand-github

related:
    includeNewer: true
    threshold: 60
    toLower: false
    indices:
        - name: tags
          weight: 100

        - name: categories
          weight: 200

markup: # **Markup Section 非常重要**
    defaultMarkdownHandler: pandoc # 选择 Pandoc
    pandoc:
        preserveTOC: false # 此选项开启则不会剔除掉 Pandoc 自己生成在文章中的 TOC,关闭则只显示 Sidebar TOC
    tableOfContents:
        endLevel: 5 # 从 5 结束
        ordered: true
        startLevel: 2 # 从 2 开始
    highlight: # 代码高亮配置,对 Pandoc 而言似乎是没用的。稍后会提到如何使代码高亮
        anchorLineNos: false
        codeFences: true
        guessSyntax: false
        hl_Lines: ""
        lineAnchors: ""
        lineNoStart: 1
        lineNos: true
        lineNumbersInTable: true
        noClasses: true
        style: dracula
        tabWidth: 4


# 使用下面的配置才能使得 Pandoc 渲染 Blog 文章

security:
  enableInlineShortcodes: false
  exec:
    allow: ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$', '^p']
    osEnv: ['(?i)^(PATH|PATHEXT|APPDATA|TMP|TEMP|TERM)$']

文件架构的差异

  • 在先前的文章中提到过 Hugo 与 Hexo 文件管理的差异

Hugo 的内容管理,每篇文章独立项目文件夹

├── post (主文件夹)
│   └── operator-precedence (项目文件夹)
│       ├── index.md (文章内容)
│       └── operator-precedence (图像资源)
│           ├── c-operator-precedence.webp
│           └── tree.webp

Hexo 的内容管理,每篇文章都在 _posts 主文件夹下

_posts (主文件夹)
├── acsl (文章图像资源)
│   ├── ...
│   └── xxx.webp
├── acsl.md (文章内容)
├── ap-cal-bc
│   ├── ...
│   └── xxx.webp
├── ap-cal-bc.md
├── ap-chem
│   ├── ...
│   └── xxx.webp
└── ap-chem.md

文件头部配置 Front Matter 的差异

  • 这是本文的 Front Matter
title: "如何优雅的从 Hexo 转移 Blog 到 Hugo"
date: 2022-02-17T14:47:22+08:00
lastmod: 

author: WetABQ
description: '详细记录了本 Blog 从 Hexo 迁移至 Hugo 的过程。'
tags: [Hugo, Hexo, WetABQ]
categories: Blog
image: index/hexohugo.webp

license: true
toc: true
comments: true
mathjax: true
print_background: true
puppeteer:
  timeout: 1000
  • 其中包含如下变动
    • date 相较于 Hexo 更加严谨
    • update \(\to\) lastmod
    • image 为文章封面
  • (针对 Stack 主题)
    • toc 目录开关
    • license 协议开关
    • comments 评论开关

关于 print_backgroundpuppeteer 参数

  • slug 在 Markdown 文件头部(Front Matter)已配置时,则使用配置内的名字,否则 slug 等于 filename 参数即文件名

  • 具体参考 Hugo 官方文档

RSS 订阅

Hugo 默认的 RSS 地址为 /index.xml,而 Hexo 则是 /atom.xml

Hugo 在 RSS 比 Hexo 实现更为简单,不会过滤掉文本内本身的非法字符。建议通过 XML 自动解析插件先访问一遍站点的 /index.xml 链接,检查是否能够正常解析。如果不能,通常是由于非法字符导致的,比如非常魔怔的 Unicode Character 'BACKSPACE' (U+0008),只要任何文章中有一处非法字符都会导致整个站点的 RSS 订阅失效。

针对 Stack 主题做的一些修改

使用 MathJax 而非 KaTeX

参考文章:Render LaTeX math expressions in Hugo with MathJax 3

themes/hugo-theme-stack/layouts/partials 下新建 mathjax.html 文件,内容如下:

<script>
  MathJax = {
    tex: {
      inlineMath: [['$', '$'], ['\\(', '\\)']],
      displayMath: [['$$','$$'], ['\\[', '\\]']],
      processEscapes: true,
      processEnvironments: true
    },
    options: {
      skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre']
    }
  };

  window.addEventListener('load', (event) => {
      document.querySelectorAll("mjx-container").forEach(function(x){
        x.parentElement.classList += 'has-jax'})
    });

</script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script type="text/javascript" id="MathJax-script" async
  src="https://cdn.jsdelivr.net/npm/[email protected]/es5/tex-mml-chtml.js"></script>

然后在 themes/hugo-theme-stack/layouts/partials/head/head.html 的最后加入这段代码:

{{ if .Params.mathjax }}{{ partial "mathjax.html" . }}{{ end }}

高亮代码

同样地,在 themes/hugo-theme-stack/layouts/partials/head/head.html 的最后加入这段代码

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/base16/darcula.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

Light Mode 配色

Stack 原生的 Light Mode 配色实在是太瞎了

From Example Site of Stack Theme

themes/hugo-theme-stack/assets/scss/variables.scss 进行如下修改

  • 修改第 33#bababa#404040
  • 修改第 73#bababa#808080
  • 134 - 135
--code-background-color: rgba(0, 0, 0, 0.8);
--code-text-color: #ffffff;
  • 141 - 142
--code-background-color: #4c4c4c;
--code-text-color: ##dddddd;

TOC 无法跟随滚动

themes/hugo-theme-stack/assets/ts/gallery.ts 中修改 69-77 行,在外套一个 null 值判断

if (paragraph != null) {
  if (paragraph.textContent.trim() == '') {
      /// Once we insert figcaption, this check no longer works
      /// So we add a class to paragraph to mark it
      paragraph.classList.add('no-text');
  }
  
  let isNewLineImage = paragraph.classList.contains('no-text');
  if (!isNewLineImage) continue;
}

移除 PhotoSwipe

PhotoSwipe 对 Pandoc 渲染文章的图像不太兼容,点击图像会导致全站卡死

themes/hugo-theme-stack/layouts/partials/article/components/photoswipe.html 内代码全部删除

移除相关文章中的彩色遮罩

这彩色遮罩实在是太丑了

直接移除 themes/hugo-theme-stack/assets/scss/partials/layout/article.scss299 - 304 行代码,

&.has-image {
  .article-details {
      padding: 20px;
      background: linear-gradient(0deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0.75) 100%);
  }
}

进一步移除 themes/hugo-theme-stack/assets/ts/main.ts30-59

友情链接与归档页面中图像圆角

themes/hugo-theme-stack/layouts/partials/article/components/links.html18 - 22 行替换为

{{ with $link.image }}
    <div class="article-image">
        <img src="{{ . }}" style="border-radius: 50%;" loading="lazy">
    </div>
{{ end }}

themes/hugo-theme-stack/layouts/partials/article-list/compact.html29-35 行替换为

<img src="{{ $Permalink }}" 
      width="{{ $Width }}"
      height="{{ $Height }}" 
      alt="{{ .Title }}"
      loading="lazy" style="border-radius: 10%;">
{{ else }}
  <img src="{{ $image.permalink }}" loading="lazy" alt="Featured image of post {{ .Title }}" style="border-radius: 10%;" />

添加文章内的链接样式

因为 Pandoc 渲染不经过主题的 Markup Hook,无法由主题对 a 类型标签自动添加 class

themes/hugo-theme-stack/assets/scss/style.scss 最后添加

.article-content {
    a {
        text-decoration: none;
        color: var(--accent-color);
        box-shadow: 0px -2px 0px rgba(var(--link-background-color), var(--link-background-opacity)) inset;
        transition: all 0.3s ease;

        &:hover {
            color: var(--accent-color-darker);
            box-shadow: 0px -10px 0px rgba(var(--link-background-color), var(--link-background-opacity-hover)) inset;
        }
    }
}

使用

当迁移完成后,我们可以通过 hugo server 在本地的 http://localhost:1313/ 中实时预览

自动化发布与部署

SDLMoe 是通过 Cloudflare Pages 和 Gitlab 协同自动化部署

当提交 Commit 修改 Markdown 文件时,Cloudflare Pages 会自动化的拉取 Gitlab 上的仓库并运行 Hugo 进行渲染输出

这是自动化部署容器所使用的脚本 build-ci.sh

#!/bin/bash

curl -Lo hugo-bin https://github.com/SDLMoe/hugo/releases/latest/download/hugo
curl -Lo pandoc https://github.com/SDLMoe/pandoc-bin/releases/latest/download/pandoc

chmod +x hugo-bin
chmod +x pandoc

PATH=$(pwd):$PATH ./hugo-bin --minify --gc

本地 build-local.sh

#!/bin/bash

./hugo-bin --minify --gc
comments powered by Disqus