Hexo to Hugo
文章目录
是的,我回来了。停更了 4 个月之后,再次开始更新这个写了 14 年的博客。
停更主要有两个原因:
- 太忙(lan)
- Hexo 太慢
原因并不重要,直接看迁移的过程吧!
1. 为什么要迁移到 Hugo
我曾经在 2014 年 博客静态化工作 时写了一个工具 wpcmd 用来实现 Wordpress 的本地发布。3 年后的 2017 年,我花了一些时间把 WordPress 迁移到了 Hexo 1/2。现在,我要从 Hexo 迁移到 Hugo。
转到 Hexo 的这两年时间,我用得并不顺畅。主要原因有下面三个:
- 我的博客现在有接近 900 篇文章,在我的 MacBook Pro (Retina, 13-inch, Early 2015, 3.1 GHz Intel Core i7, 16 GB 1867 MHz DDR3)上,每次使用 Hexo 编译都超过 2 分钟。如果在我的 1C2G 服务器上编译,时间将会更长。这个时长是无法忍受的。
- Hexo 的文档不全,在需要对其进行扩展的时候,经常需要去读源码。对于一个工具来说,这降低了使用效率。虽然我仍然是一个程序员,但我不认为在这个工具上花时间是值得的。
- Node.js 的工具链我一直不太喜欢。安装慢,依赖复杂,而且容易出错。
所以当我看到 Hugo 这个号称 The world’s fastest framework for building websites 的静态化工具时,不动心是很难的。部署容易,使用简单,还能顺便学一下 Go 语言,简直是为我量身打造的工具了。
事实证明,切换到 Hugo 之后,构建我的整个博客,只需要 10 秒多点,那叫一个快!
1 | EN
2+------------------+------+
3 Pages | 1431
4 Paginator pages | 268
5 Non-page files | 0
6 Static files | 1009
7 Processed images | 0
8 Aliases | 1133
9 Sitemaps | 1
10 Cleaned | 0
11
12Total in 10549 ms
下面就按照时间顺序来叙述。
2. themes
maupassant 简洁美观,目前我使用这款。后面可能会换成 capsule。
3. URL 对齐
网站迁移的过程,最重要的就是不能影响之前的 SEO。在从 WordPress 迁移到 Hexo 的时候,由于要切换域名,我在 Nginx 上做了一些工作来保证 SEO 正常。这一次,要做的工作也不少。我依然采用了和 WordPress 相同的结构来处理博客。
3.1 POST 和 PAGE
- 使用
/post/{\d+}.html
作为博客文章的 URL,对应 WordPress 中的post
,例如:https://blog.zengrong.net/post/2635.html
- 使用
/{\w+}/
作为博客页面的 URL,对应 WordPress 中的page
,例如:https://blog.zengrong.net/wpcmd/
对于 post
,我将每篇文章的 Markdown 文件放在 /content/post/
路径下,文件名保持原来的 {\d+}.md
不变。他们的 type
都是 post
,对 slug
和 aliases
的处理保证了这个页面的老地址也能被访问。
这样处理之后,该页面的地址变成了: https://blog.zengrong.net/post/wordpress-to-hexo1/
,但是之前的页面地址 https://blog.zengrong.net/post/2635.html
依然可以访问,并会自动跳转到新的地址。这个转向并不是在服务端做的,而是在客户端生成了一个转向界面完成的。搜索引擎将识别这个跳转,在今后漫长的时间里,把 SEO 转到新的地址。
下面是 /content/post/2635.md
这个页面的 Front Matter
内容:
1+++
2title = "WordPress to Hexo(1)"
3postid = 2635
4date = 2017-05-24T23:08:36+08:00
5isCJKLanguage = true
6toc = true
7type = "post"
8slug = "wordpress-to-hexo1"
9aliases = [ "/post/2635.html",]
10category = [ "web",]
11tag = [ "wordpress", "master", "hexo", "staticize",]
12lastmod = 2017-05-24T23:08:36+08:00
13+++
对于 page
,我将每篇页面的 Markdown 文件放在了 /content/page/
路径下,文件名和原来的一致。它们的 type
都是 page
,对 slug
和 url
进行了处理。
根据 Hugo 的约定,/content/page/
这个 Section 的文件会生成在 /page/{\w+}/
URL 下面,我通过制定 url
来让页面被直接生成到根目录下。
下面是 /content/page/wpcmd.md
这个页面的 Front Matter
内容。
1+++
2title = "WPCMD"
3postid = 2321
4date = 2015-06-12T09:40:22+08:00
5isCJKLanguage = true
6toc = true
7type = "page"
8slug = "wpcmd"
9url = "/wpcmd/"
10+++
当然,手工来做这件事愚蠢且耗时,我写了一个 Python 脚本来完成这些自动转换: hexo2hugo.py
3.2 Category 和 Tag
我之前使用的 Category 和 Tag 的单数形式,因此我的标签页地址为: https://blog.zengrong.net/tag/study/
。而在 Hugo 中采用了复数模式,默认的标签页地址是: {domain}/tags/xxx/
。
要解决这个问题,可以在 config.toml 中进行如下配置:
1[taxonomies]
2 category = "category"
3 tag = "tag"
但是,配置完成后,文章中的分类和标签都无法显示了。
这是因为在 theme 中写死了对变量 .Params.Categories
和 .Params.Tags
的引用。将模版中这样的变量都改为单数形式即可。
通过修改了下面几个模版中,解决了上面的问题:
maupassant/layouts/index.html
maupassant/layouts/_default/single.html
maupassant/layouts/_default/taxonomy.html
maupassant/layouts/partials/categories.html
maupassant/layouts/partials/tags.html
4. 嵌入标签处理
在 Hexo 中,我定义了几个嵌入式标签:
- flash 用于嵌入 flash 动画
- download 用于支持下载链接管理
- label Hexo 主题中自带,用于实现带颜色的 label 效果
在 Hugo 中可以使用 Shortcodes 来实现它们。我写了 3 个 Shortcodes 来实现这些功能。
当然,需要批量转换 Hexo 格式的嵌入标签到 Hugo 格式的 Shortcode 。这不难,用 Python 正则替换一下就好。这部分替换脚本也在 hexo2hugo.py 中有提供。
5. 留言服务
上次将博客迁移到 Hexo 的时候,我选择的评论服务是国内的畅言。因为当时多说濒临倒闭,Disqus 在国内又被墙。现在来看这个选择挺糟糕的,畅言的乱七八糟广告特别多,接入后我的博客就变成了牛皮癣广告墙,这里一块那里一块。
我在 WordPress to Hexo(2) 中提到过,希望使用基于 Github 的 Issue 系统的评论系统来完成评论。这样的系统现在还不少的:
使用它们存在一个问题,博客现有的三千多条评论就没法转换过去了。
因此,我还是选择了需要自己部署的产品 Isso。Python 实现,自己也可以随便折腾。
5.1 uwsgi.ini
Isso 的架设过程并不复杂,因为对 uWSGI 已经很熟悉了,我自然是选择 uWSGI 作为生产环境的部署,下面是我的 uwsgi.ini
配置文件。
1[uwsgi]
2;socket = %d%n.sock
3http = 127.0.0.1:1314
4master-fifo = %d%n.fifo
5pidfile = %d%n.pid
6
7master = true
8processes = 2
9threads = 2
10max-requests = 600
11chmod-socket = 666
12thunder-lock = true
13harakiri-verbose = true
14harakiri = 10
15buffer-size = 32768
16
17ignore-sigpipe = true
18ignore-write-errors = true
19disable-write-exception = true
20
21req-logger = file:%dlogs/req.log
22logger = file:%dlogs/%n.log
23daemonize = %dlogs/%n.log
24log-master = true
25threaded-logger = true
26
27env = ISSO_SETTINGS=%disso.cfg
28env = ISSO_CORS_ORIGIN=*.zengrong.net
29spooler = %dspooler
30wsgi = isso.run
31venv = %dvenv
32chdir = %d
33uid = app
34gid = app
5.2 isso.cfg
isso.cfg 配置文件的内容如下:
1[general]
2name = blog
3dbpath = /xxx/comment.db
4host = https://blog.zengrong.net/
5log-file = /xxx/isso.log
6notify = smtp
7reply-notifications = true
8gravatar = true
9
10[smtp]
11username = zrong
12password = xxxxxx
13host = mail.xxx
14port = 111
15security = ssl
16to = i@zengrong.net
17from = i@zengrong.net
18timeout = 5
19
20[moderation]
21enabled = true
22
23[hash]
24salt = xxxxxxxxxxxxxxxxxxx
5.3 CROS Bugs of isso
isso 让人很迷的有几点:
- 环境变量中配置的
ISSO_CORS_ORIGIN
无效; - 配置文件中配置的
host
用来指定跨域信息的域名也无效; - 配置文件中配置的
log-file
无效,看不到 isso 的日志。
我在跨域的问题上纠结了许久,最后还是决定把 isso 部署到博客所在的 URL 子域下以规避跨域问题。这应该是 isso 的 bug,等有闲工夫了,我再去修改 isso 的源码来解决。
5.4 迁移畅言的历史评论到 isso
在畅言后台,可以把历史评论记录导出为 json 文件,我用 Python 写了一个脚本: changyan2isso.py 将导出的数据转换成为 isso 可以导入的标准 json 格式。然后使用 isso import
命令将其导入到 isso 的数据库中。
isso 也可以导入 WordPress 和 Disqus 的 XML 格式数据。
isso 导入 json 文件的时候,会忽略留言数据的 parent
属性,这个属性用来指引留言的回复关系。我曾经修改 changyan2isso 使其支持 parent
属性,但回复关系依赖被回复的留言必须优先存在。而我在处理历史数据的时候并没有考虑这一点。折腾了一阵子之后,我不打算在十几年的老回复上再浪费时间,就此作罢。
有兴趣的同学,可以根据 这段代码,配合 isso 中的 import 包来解决这个不支持 parent 的问题。当然,你也可以把历史回复导出成 WordPress 或者 Disqus 格式然后再导入。
我不太喜欢 XML,所以,就酱紫了。
- 文章ID:2677
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/hexo-to-hugo/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。