Hexo博客迁移到hugo:踩坑&小记

Hexo博客迁移到Hugo:踩坑&小记

动机

这个博客一直是本地生成然后通过git push直接部署,之前用的hexo(见当初的博文)。但是hexo基于nodejs,实在是太慢了,博客功能和文章数量上来之后生成一次要几分钟,而且风扇一直狂转。

hugo打出的旗号是“The world’s fastest framework for building websites ”,据说生成一次只需要几秒,对我有很大的吸引力。另外之前使用的hexo主题已经几乎不维护了,想着换个主题不如直接把生成器也换了。

理想是美好的,现实是残酷的。在迁移的过程中也花了好一份功夫,踩了许多坑,所以特此记录。

从hexo迁移到hugo

目标:

  • 尽量不减少对博客源文件的修改
  • 尽量避免在源文件中使用特定于hugo/主题的特殊语法

hugo目录结构

详细目录结构介绍见hugo文档,这里提几个关键的目录:

  • content:存放文章markdown源文件
  • static:静态资源,static/*会直接复制到站点根目录
  • themes:主题文件夹,用于存放主题
  • public:生成输出目录

front matter兼容性

从hexo迁移时,hugo可以重用hexo中的大部分yaml front matter。但有如下几个注意点:

  • hugo使用的date字段语法不同,但hexo的日期格式实测hugo也兼容,一个小细节是对于新创建的文章,date字段最后最好加上时区+0800,否则可能导致当天创建当天发布的文章日期被认为是未来从而在博客中无法看到。

  • categories需要使用列表语法,如果只有一个分类也要使用中括号包裹,例如

    1
    
    categories: [笔记]

    否则会出现如下报错:

    1
    
    executing "_default/summary.html" at <.Params.categories>: range can't iterate over 笔记
  • 对于部分主题,tags列表不能写出但留空。也就是说,像下面这样的写法在某些主题会报错:

    1
    2
    
    tags:
      - 

保持链接一致/重定向

原先的hexo博客生成的URL为//example.com/category/filename.html。为了保持链接一致,确保文章被其他地方引用的链接仍然有效,需要hugo生成的URL也保持一致,或者通过重定向将原链接导流到新的链接。

方案零、配置hugo保持URL一致

文章源文件需要按原有结构放在content/[category]下。除此之外,按照hugo的配置文档hugo URL文档,还需要在config.toml中设置如下配置:

1
2
uglyURLs = true #保持.html后缀
disablePathToLower = true #关闭路径默认转小写

考虑到不带.html的链接确实更加简洁美观,同时我在使用hugo时将所有文章都放在了content/posts目录下,这样可以和content/about等顶级页面保持一致,因此决定放弃直接与原有的链接格式保持一致。但这样为了保证原有链接仍然可用,需要设置页面重定向。

重定向方案一、使用hugo提供的alias配置项

hugo URL文档中提到可以使用alias这一front matter来方便地自动生成重定向页面:

1
2
3
aliases:
- previous-file-name
- original-file-name

重定向方案二、手动生成重定向页面

但对于迁移博客的我来说,使用alias需要按一定规则批量修改源文件的front matter,实现起来较为麻烦,并且我不希望将源文件搞得太丑陋。因此考虑在其他地方手动生成重定向页面。

hugo文档中给出了重定向页面的html内容,可以据此手写脚本,遍历原有的文件夹结构批量生成重定向页面文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import os
def html_gen(newPath): #https://gohugo.io/content-management/urls/#how-aliases-work
    return f'''
<!DOCTYPE html>
<html lang="en-us">
  <head>
    <title>{newPath}</title>
    <link rel="canonical" href="{newPath}">
    <meta name="robots" content="noindex">
    <meta charset="utf-8">
    <meta http-equiv="refresh" content="0; url={newPath}">
  </head>
</html>'''

import os

def generate_html_files(posts_dir, output_dir):
    for subdir, _, files in os.walk(posts_dir):
        for file in files:
            if file.endswith('.md'):
                md_path = os.path.join(subdir, file)
                relative_path = os.path.relpath(md_path, posts_dir)
                html_path = os.path.join(output_dir, os.path.splitext(relative_path)[0]) + '.html'
                os.makedirs(os.path.dirname(html_path), exist_ok=True)
                newlink = "/posts/" + os.path.splitext(relative_path)[0].lower()
                with open(html_path, 'w') as f:
                    f.write(html_gen(newlink))
                print(md_path,'->',html_path,f'({newlink})')
generate_html_files('.', '../../static')

将上面生成的文件放置在static目录下,即可在站点部署后将旧的URL重定向到新的URL。

Git Deploy

部分云托管平台提供了通过git仓库进行静态站点托管的功能,只要推送生成后的public到指定仓库分支,就会同步更新托管站点。但是我使用的云并不支持免费的持续集成,无法直接通过hugo仓库推送执行自动生成和部署。所以目前只能采取本地生成然后push生成后的站点文件。

hexo中,简单配置即可在hexo d时自动push到指定的git仓库,但是hugo并不支持,也没有提供类似生成后hook的功能。

为了能够在生成之后一键push实现部署,仿照hexo-deploy的实现方式,创建一个隐藏仓库.DeployPublic,每次更新时将public的内容复制进去并提交。

(之所以不直接在public建立git仓库,是因为hugo生成时并不会自动删除无用文件,需要在config.toml中设置cleanDestinationDir = true或者每次删除public文件夹确保每次生成的站点资源都是干净的)

将上述操作写成一个shell脚本(已上传Gist),以后就可以通过这个脚本执行实现一键生成/部署。

Valine数据迁移

我自己是没有太多数据,就直接手动修改url了。但LeanCloud提供了数据批量操作,可以按条件自动处理数据。

hugo有关注意事项

多语言设置

在config.toml中设置defaultContentLanguage=‘zh-CN’设置中文。

对于部分主题,需要这个配置来决定一些默认文字的显示语言。

页面内自定义HTML

hugo默认配置下会忽略markdown中插入的HTML。根据hugo文档,需要开启以下设置允许md文件中的HTML内容:

1
2
[markup.goldmark.renderer]
	unsafe= true

注意开启上述配置后,仍然不能在md中直接使用<script>标签插入脚本。可以看看主题是否对此有支持。例如我使用的LoveIt/FixIt主题提供了这一shortcode,支持插入js脚本。

使用gitinfo更新lastmod

根据hugo配置文档,hugo除了显示文章的发布日期外,还支持显示文章最后修改的时间。需要在config.toml中启用如下设置:

1
enableGitInfo = true

但注意,默认情况在hugo serve环境即使启用了上述设置,这个功能在页面内也并不会启用。如果需要在hugo serve预览,还要运行时带上参数–enableGitInfo,即:

1
hugo serve --enableGitInfo

⚛数学公式

这部分说来话长,写在另一篇文章

关于hugo的吐槽

一开始换用hugo是看中它性能高,但用下来发现性能并没有想象的那么优秀。在主题稍微复杂,文章百来篇的情况下,生成一次的时间大约在8~30s浮动,并不像宣传说的几秒内就能出结果。

hugo另一个缺点是扩展性差。对于hexo,在任意阶段有任何需求都可以通过编写插件解决,而hugo则除了官方提供的功能几乎什么都干不了。甚至对于原本就提供插件功能的goldmark渲染器,hugo也并不支持安装其插件。

另一个感受与hugo本身无关,是在查阅各种issue/社区/文档时感受到的官方维护人员的傲慢。例如前面提到的数学公式转义问题,在几年来被大量、反复提及,但官方一直没有解决的意向。hugo的文档也不太友好,各种配置散落在各个页面,并且对于本来就不熟悉hugo的人来说其中的很多概念也解释得并不清楚。


说明:以下部分开始与hugo无关。

使用极狐作为图床

2024更新:极狐已收费。

目前常见的图床方案及其缺陷有如下几种:

  • 第三方图床:链接格式混乱,数据难管理,可靠性难以保证
  • 云服务商的对象存储:具有存储/流量的费用
  • 使用Github仓库:国内访问不畅

我原来的博客中的图片直接与生成的站点文件一起传在托管服务上,但是这部分文件占用大,并且用户访问、下载的时候还会产生额外的CDN流量费用。

极狐是gitlab在国内的本地化服务。可以使用极狐作为免费图床,来避免上述问题。步骤如下:

  1. 注册和新建一个仓库。注意新建仓库的时候不要根据提示创建群组,直接在用户空间下创建,除非你想要一个不同于用户名的前缀。
  2. 整理博客中的图片,在本地新建git仓库并上传。
  3. 将仓库设为公开,这样才能在博客网页中直接访问原始内容。
  4. 替换文章中使用的图片链接为极狐的raw链接。
  5. 本地启动hugo serve并查看图片是否加载正常。

特别注意 :极狐作为gitlab在国内的本地化服务,其上存储的内容受到审查。例如当你存放了某些电影的剪辑片段时,在博客网页会加载失败,在极狐仓库页面会显示内容因法律原因不予显示。

网站图标favicon

hugo可以将favicon放在/static目录, 生成时会自动复制到站点根目录。

可以用这个网站https://realfavicongenerator.net/从一张图片生成各种大小、浏览器、PWA等所使用的图标文件。

0%