博客从 Hexo 迁移到 Hugo 的过程

2023年8月4日 · 6311 字 · 13 分钟 · #好玩系列

从今天开始,博客进入 2.0 版本。相比 1.0 版本,有了很多的新东西。

  1. 换了名字,这是我一直想做的事;
  2. 主题更清爽,Hexo 是简洁,但颜值一般;
  3. 新增评论、留言、赞助等功能;
  4. 整体结构更加简洁,更易修改。

以此总结一下在更换过程中遇到的问题,同时补充针对 guangzhengli/hugo-theme-ladder的修改记录,方便查阅。

v2.0 版本首页
v2.0 版本首页

本文并非从 0 到 1 的记录,所以不会提供很详细的截图;想详细了解的可以根据温中资料查阅,或者充分利用搜索引擎、AI 等产品哈;截图相对比较费时,我就偷个懒。

为什么想换

如果是「爱好折腾」占 70% ,那剩下 30% 一定是真需要换的理由。哈哈

现状:

  • 旧博客的主题是 5.X 版本,目前最新 8.X 版本,如果要升级,需要调整的内容太多,我曾两次尝试,均以失败告终,还是选择了放弃。
  • 旧的博客用了很长一段时间,期间改了不少的东西,我甚至忘记改过什么,每次调整都很麻烦,积累了太多的「bug」就会带来很多新「bug」,所以一直保持现状。
  • 很多功能都缺失,比如留言、评论等;如果在原来基础上加的话,根据「第二条」阻力过大。
  • 从 2016 年 - 2023 年,审美确实疲劳,换个好看一点的不过分吧?

机缘巧合,看到了 @GuangzhengLi 向量数据库文章,发现了这个 Blog 的主题是我喜欢的风格,非常简单明了,于是就有了换博客的想法。

v1.0 版本首页
v1.0 版本首页

哦,对了,1.0 版本我是和 Jason,独立开发者,自由职业者 | Jason 学习的。

迁移过程

了解新的框架和主题

从原理上来说,Hexo 和 Hugo 同属一种静态博客,写 Markdown 文件就可以完成部署,这是我为什么喜欢用 Hexo 的原因之一,在我能力范围内,它做到了足够简单;文件存储方式简单,部署方式简单。同样,Hugo 与它同理;但是我并没有实际安装和部署过,也是从头开始。好在 @GuangzhengLi 写了《如何 30 分钟搭建一套完整独立博客》 的文章,由于我没有安装过 Hugo ,所以也结合了 @Elizen 的 《使用 Hugo 从 0 到 1 搭建个人博客》 。此次能完成迁移,这两篇文章起到了至关重要的作用。

注册账号等流程我就省略了,可以参考 @Elizen 的文章,更详细。

第一步,先完成 demo 版的部署,快速找到「成就感」。访问 guangzhengli/hugo-ladder-exampleSite ,然后根据 README.md 就可以快速拥有一个线上的博客了。但是,但是,这里有一个小的问题 🙋‍♂️,我踩的第一个坑;流程并不是按照该文档说明的那样简单,在 Action 的时候就出现报错,具体如下:

Push the commit or tag
/usr/bin/git push origin gh-pages
remote: Permission to username/username.github.io.git denied to github-actions[bot].
fatal: unable to access 'https://github.com/username/username.github.io.git/': The requested URL returned error: 403
Error: Action failed with "The process '/usr/bin/git' failed with exit code 128"

我通过 ChatGPT 问了很久,都没有找到答案,最后通过 Google 搜索,3s 就解决了问题。ChatGPT 让我配置权限,或者检查是否存在对应分支等。搜索的关键词是 https://github.com/ad-m/github-push-action/issues/96 ,结果页第一个链接就可以解决,解决方案参见:remote: Permission to git denied to github-actions[bot]. · Issue #96 · ad-m/github-push-action,至此第一步已经完成。

这里也让我有非常大的收获:最近长期依赖 ChatGPT 等 AI 工具,导致我对搜索引擎使用频次大幅下降;两者的区别很大,前者类似我提出问题获得了一个需要我验证的答案,然后来回测试,这个过程我会越来越相信 AI ,而忘记了「思考」,成为了一个不需思考的执行者;后者我需要辨别,搜索结果页有很多的答案,我可以看到谁使用了什么方法达到了什么结果,对我来说是否合适?这个过程我需要判断和记忆,而非简单的执行。所以,后续我还是需要结合搜索引擎来寻求答案。

没有说 ChatGPT 不好用的意思,在后续的步骤,它起到了非常重要的作用。

第二步,本地安装环境,将 demo 部署到本地。这里就需要用到 @Elizen 的入门文章了,步骤也比较简单,会涉及到 Git 操作、版本兼容、网络等问题,一般搜索都可以解决,不再赘述啦。

准备定制化修改

第一步,先熟悉目录结构和配置文件(根目录下的config.yml)。这个非常重要,因为所有的修改都是基于这个来的;如果不了解你想要修改的框架,后续将寸步难行。别慌,一个个来,先看配置文件。

baseURL: 'https://hugo-ladder.pages.dev' #修改为你的 https://username.github.io
homepage: 'https://hugo-ladder.pages.dev' #修改为你的 https://username.github.io
params:
  brand: HOME # 修改默认的为自己网站的标志
  avatarURL: /images/avatar.png #网站主页的照片信息,你可以在根目录 /static/images/ 里面替换成自己的照片
  author: Hugo Ladder # 修改你自己的名字
  authorDescription: # 修改对你自己的描述
  info:  # 修改对你自己网站描述
  favicon: /images/avatar.png #网站的 icon,你可以在根目录 /static/images/ 里面替换成自己的照片
  options:
    showDarkMode: true # 是否支持黑暗模式
 languages: # 如果你启用多语言,下面是中文展示
  zh:
    languageName: 
    author: Ladder 主题
    authorDescription: 一个美观,快速并且专注于阅读的主题
    info: 帮助开发者构建一个免费并且漂亮的博客网站,记录自己的思考并且提高自己的影响力

上面这个配置文件是 @GuangzhengLi 在安装文档提到了,备注已经写了,问题不大哈。我在此基础补充一份完整的配置文件介绍,这个是来自 ChatGPT 4.0 的总结。

👈 点击查看
- `baseURL`: 网站的基础URL,用于构造绝对URL。
- `title`: 网站的标题,常用于网站标题栏或头部。
- `theme`: 指定Hugo站点使用的主题。
- `license`: 网站使用的许可证类型。
- `licenselink`: 许可证的链接地址。
- `description`: 网站的描述信息。
- `homepage`: 网站主页的URL地址。
- `defaultContentLanguage`: 网站的默认内容语言。
- `googleAnalytics`: 用于Google Analytics的配置(值缺失)。
- `paginate`: 网站分页中每页的文章数量。
- `hasCJKLanguage`: 是否包括中日韩文字。
- `copyright`: 版权声明信息。
- `Author`: 作者信息字段,包括名字和电子邮件地址。
- `menu`: 定义主菜单的结构。
- `params`: 一系列自定义参数,具体解释如下:
  - `brand`: 品牌名称。
  - `avatarURL`: 作者头像的URL地址。
  - `author`: 作者的名字。
  - `authorDescription`: 作者的描述。
  - `info`: 网站信息描述。
  - `favicon`: 网站图标的URL地址。
  - `options`: 一系列布尔选项,如是否显示深色模式,是否启用图片缩放等。
  - `darkModeTheme`: 暗黑模式主题名称。
  - `sponsors`: 赞助者相关信息,包括购买咖啡链接、微信和支付宝二维码等。
  - `comments`: 评论系统的配置,包括giscus和utteranc的选项。
  - `analytics`: 分析工具的配置,包括Google和Umami选项。
  - `guestbook`: 留言板的标题和描述。
  - `social`: 社交媒体链接和图标。
- `languages`: 网站的语言配置,可以定义不同语言下的特定内容。
- `taxonomies`: 自定义分类法的名称,例如系列和标签。

第二步,熟悉主题的目录(Hugo 目录前面文章都提到了),主题中主要熟悉 layouts 文件夹就行。具体如下:

主题文件目录结构(修改版)
主题文件目录结构(修改版)

  1. 主题文件是子模块,和 hugo-theme-ladder 保持同步,也可以用 git submodule update --remote --merge 命令进行手动更新。
  2. 自定义主题文件夹和主题文件结构一致,命名可以自己起;只是我为了能和主题更新保持同步,同时还能对主题进行修改,新建了主题;同时在config.yml配置好,就可以同时使用,渲染的时候自定义主题优先级高于原主题。
  3. 评论模块调整较少,hugo-theme-ladder 提供了两种评论系统,我屏蔽了其中一个,使用giscus
  4. 公众模块,主要是调整网站开始时间。
  5. 网站首页没有做调整,这块就是访问网站的主页。
  6. 文章结构公共部分,调整很多,特别是 meta.html 后面延伸补充。
  7. 文章列表和主题也有调整,影响列表页和详情页。
  8. 一些特色的用法,参考guangzhengli/awesome-hugo-shortcodes就行,比较简单。

定制化修改

先确定目标,定制化修改的目的是不影响现有搜索引擎和访问体验。比如原来是 demochen.com/posts/122 可以访问详情页,现在只能 demochen.com/blog/122 就不符合要求,需要调整;除此之外就是个人的爱好方面调整了。

本人不太懂这些技术实现,可能有点傻傻的,但是需求是实现了,如果你有更好的方式欢迎评论补充👏

统一文章详情页的 URL

以前的文章详情页地址是demochen.com/posts/122 ,现在主题的文件夹名不一样,是blog了,意味着原来的就会404,这是最不能接受的。调整方法如下:

  • layouts/ 下的 blog 改为 posts
  • layouts/partials/ 下的 blog 改为 posts
  • content/ 下的 blog 改为 posts
  • config.yml中的menu/main下对应的文章 url改为 /posts
- menu:
      main:
        - name: 文章
          url: /posts
  • hemes/hugo-theme-ladder-my/layouts/_default/rss.xml 中 blog 换成 posts ,确保不影响 RSS
{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if or $.IsHome $.IsSection -}}
{{- $pages = (where (where $pctx.RegularPages ".Section" "posts") "Kind" "page")  -}}
{{- else -}}
{{- $pages = (where (where $pctx.Pages ".Section" "posts") "Kind" "page")  -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}

调整文章的ID

以前博客URL是:域名 + posts + 文章ID,新的主题是 域名 + blog + 文章标题。所以尽管我们前面调整了 blog ,其实还是会 404 。原来文章的ID是在文章yaml中定义,字段是 abbrlink,需要针对七调整。具体如下:

  • 将原来的文章 Markdown复制到content/posts,同时通过 VS codeabbrlink批量替换为 slug,因为 Hugo 不支持原字段,所以需要调整。
  • 最后在config.yml文件最后加上下列代码即可
permalinks:
  posts: "/posts/:slug/"

调整博客 RSS 地址

Hugo 默认的 RSS 地址是以 域名+index.xml结尾的,比如https://guangzhengli.com/index.xml,我之前的博客的 RSS 地址是 atom.xml 结尾的,为了确保订阅的朋友还能收到更新,所以我需要调整。方法如下:

  • config.yml文件最后加上下列代码(全文输出)
outputFormats:
  RSS:
    mediatype: application/rss
    baseName: atom
    notAlternative: true

调整目录显示调整

hugo-theme-ladder默认每一篇文章都会显示目录,对于文章较短的我不希望显示目录,影响阅读体验。于是通过新增字段 no_toc 来判断是否显示目录,具体如下:

  • content/posts/ 中不想显示目录的文件里添加 no_toc: true,默认显示目录,故不需要加代码
  • 用下面的代码替换 themes/hugo-theme-ladder-my/layouts/partials/posts/toc.html文件
{{ if or (not (isset .Params "no_toc")) (eq .Params.no_toc false) }}
{{ $.TableOfContents }}
{{ end }}

调整网站的开始运营时间

hugo-theme-ladder默认是今年开始,对于迁移过来的人来说,不是很合理;于是在 themes/hugo-theme-ladder-my/layouts/partials/footer.html文件中调整了代码,将 「2016 -」添加进去了。

<p>&copy; 2016 - {{ now.Year }} <a href="{{ `` | absURL }}">{{ site.Title }}</a>

调整分类命名

Hugo 似乎有一个 bug (这是ChatGPT说的),如果要实现「相关文章」,分类名不能用中文,尽管我在config.yml中开启了hasCJKLanguage: true,依旧不行。相关文章是hugo-theme-ladder主题中的一个功能,即在文章详情页最后会根据当前文章分类推荐10篇同类型文章,也是为了增加 PV 嘛。

所以,我又改写了themes/hugo-theme-ladder-my/layouts/partials/posts/meta.html实现了我的需求;原来的博客分类结构是categories: 读书看报,有两个地方不满足:首先Hugo 的分类是数组结构,而且字段是series,其次「相关文章」不支持中文。

调整方法如下:

  • config.yml中将 taxonomiesseries的值改为categories,这样历史的文章结构不需要调整
  • 同时将文章中 categories 的结构改为「英文在前,中文在后」的数组结构,如categories: ["Random Thoughts","胡说八道"]即可,批量替换就像
  • themes/hugo-theme-ladder-my/layouts/partials/posts/meta.html 的代码如下:
<p>
  <small>
    {{ .Date | time.Format ":date_long" }}&nbsp;· {{ i18n "word_count" .WordCount -}}&nbsp;· {{ i18n "reading_time"
    .ReadingTime -}}
  </small>
{{ if .Site.Params.options.showMetaSeries -}}
<small>
  {{ with .GetTerms "categories"}}
  ·
  {{ end }}
  {{ $categories := .GetTerms "categories" }}
  {{ with index $categories 0 }}
  {{ $href := .Permalink }}
  {{ with index $categories 1 }}
  <a href="{{ $href }}" target="_blank">{{ .LinkTitle }} </a>
  {{ end }}
  {{ end }}
</small>
{{ end }}

</p>

代码逻辑:取分类中第一个名作为「相关文章」的筛选条件和跳转 URL,第二个作为显示名字。

新增图片备注信息居中

我的图片都是外链,格式如 ![v2.0 版本首页](https://dc-blog-img.demochen.com.cn/b17a1a52722f28d0d272da536d485f44.png!style:ToWebp) ,我希望其中类似「v2.0 版本首页」能在渲染后显示出来,处理方法如下:

  • /layouts/_default/_markup/目录下,新建render-image.html文件
  • 该文件的代码如下:
<figure>
    <img src="{{ .Destination | safeURL }}" alt="{{ .Text | plainify }}" {{ with .Title}}title="{{ . }}"{{ end }}>
    <figcaption>{{ .Text | plainify }}</figcaption>
</figure>

调整 meta 元素中描述部分不显示问题

首页 Description 没数据
首页 Description 没数据

这部分是 SEO 需要的一些字段,在文章详情页是没问题的,我看了作者关于这块的源代码,其逻辑也是没有问题的,但是就是无法显示。最终发现是 description字段在 config.yml中位置有问题。将其挪到 params 下后,问题解决。

新增版权声明信息

©️ 版权声明示例
©️ 版权声明示例
原来博客有这个,新增是别人抓取数据的时候可以通过此信息补充文章地址。

添加方法:

  • hemes/hugo-theme-ladder-my/partials目录下新增copyright.html文件,代码如下:
<div class="copyright">
    <style>
        .copyright {
            margin: 20px 0;
            padding-left: 20px;
            border-left: 5px solid #eee;
            color: #666;
            quotes: none;
        }

        .copyright:before {
            content: open-quote;
        }

        .copyright:after {
            content: close-quote;
        }
    </style>
    <p>
        作者:<a href="{{ .Site.Params.homepage }}">{{ .Site.Author.name }}</a><br />
        链接:<a href="{{ .Permalink }}">{{ .Permalink }}</a><br />
        声明:除非另有声明,本文采用 <a href="{{ $.Site.Params.licenseLink }}" target="_blank">{{ $.Site.Params.copyrightLicense }}</a> 协议,转载请注明。
    </p>
</div>
  • config.yml文件夹中,新增协议许可配置文件,代码如下:
params:
    copyrightLicense: "CC BY-NC-SA 3.0"
    licenseLink: "https://creativecommons.org/licenses/by-nc-sa/3.0/cn/"
  • 在文章模版文件themes/hugo-theme-ladder-my/layouts/posts/single.html找到文章结尾的标签,新增第二行代码
<section class="blog-content">{{ .Content }}</section>
{{ partial "copyright.html" . }} //新增这一行代码

底部新增友情链接

我目前的友情链接用的是文章的形式,方便自己自由编辑,但是我自己也有一些其他的站,还是希望在底部增加一些友情链接,作为 SEO 导流的效果。添加方法如下:

  • config.yml 添加友情链接
# 友情地址
params:
  links:
    - name: "特立独行的异类"
      url: "https://www.demochen.com"
    - name: "今天长这样"
      url: "https://today.demochen.com"
    - name: "DemoChen's Clip"
      url: "https://clip.demochen.com"
    - name: "DocHub"
      url: "https://www.dochub.wiki"
  • themes/hugo-theme-ladder-my/layouts/partials/footer.html中新增代码
<footer class="footer">
    <p>....... since 代码     </p>
<!--添加下方代码-->
    <p>❤️ 其他站:
        {{ $totalLinks := len $.Site.Params.links }}
        {{ range $index, $link := $.Site.Params.links }}
        <a href="{{ $link.url }}" target="_blank">{{ $link.name }}</a>{{ if lt $index (sub $totalLinks 1) }}、{{ end }}
        {{ end }}
    </p>
</footer>
  • 根据路径 themes/hugo-theme-ladder-my/assets/scss/ 新建_footer.scss文件,重写主题关于这部分的样式,代码如下
.footer {
    display: flex;
    flex-direction: column; // 修改了这里
    align-items: center;
    justify-content: center;
    height: 8rem;
    width: 100%;
    padding-bottom: 1rem;
    font-size: 80%;
    margin-top: auto;
}

.footer p {
    margin: 0.1rem 0;
    /* 上下间距为 0.5 rem,左右间距为 0 */
}

菜单栏新增 new 标签

若当月文章有更新,则顶部「文章」菜单显示 new,提醒读者有更新。主要修改 themes/hugo-theme-ladder-my/layouts/partials/header.html文件,代码如下:


<ul class="navigation-list" id="navigation-list"> <!--加在这个后面-->
    <!-- 20230806 add icon news  start-->

    {{ $currentMonth := now.Format "2006-01" }}
    {{ $hasNewArticle := false }}
    {{ range where .Site.RegularPages "Section" "posts" }}
    {{ if eq $currentMonth (dateFormat "2006-01" .Date) }}
    {{ $hasNewArticle = true }}
    {{ end }}
    {{ end }}
    <!-- 20230806 add icon news  start-->
    
   {{ with .Site.Menus.main}} <!--加在这个前面-->

<!--分隔符-->

<li class="navigation-item navigation-menu">
    <a class="navigation-link" href="{{ index (apply (slice .URL) (.Params.urlFunc | default "relLangURL") ".") 0 }}">{{ .Name }} <!--加在这个后面-->
   <!-- 20230806 add icon news  -->
    {{ if and $hasNewArticle (eq .Name "文章") }}
         <span class="new-icon-desktop" style="color: red; font-size: 10px; position: absolute; top: -12px; right: -19px; display: none;">new</span>
         <span class="new-icon-mobile" style="color: red; font-size: 10px; position: absolute; top: -10px; right: 103px; display: none;">new</span>
     {{ end }}       
   <!-- 20230806 add icon news  start-->  <!--加在这个前面-->
    </a>
</li>

<!--分隔符-->

<!-- 20230806 add icon news 加在最后 -->
<script>
    function adjustNewIcon() {
        var newIconDesktop = document.querySelector('.new-icon-desktop');
        var newIconMobile = document.querySelector('.new-icon-mobile');
        if (window.innerWidth <= 768) {
            newIconDesktop.style.display = 'none';
            newIconMobile.style.display = 'inline';
        } else {
            newIconDesktop.style.display = 'inline';
            newIconMobile.style.display = 'none';
        }
    }

    window.addEventListener('resize', adjustNewIcon);
    window.addEventListener('load', adjustNewIcon);
</script>
<!-- 20230806 add icon news  start-->

首页推荐阅读末尾引导到列表

考虑到首页只有推荐的文章,列表可能会被忽视,故添加了这个功能。需要修改 themes/hugo-theme-ladder-my/layouts/partials/home/profile.html文件,代码如下:

<div class="featured">
    {{ partial "home/featured.html" . }}    
     <!-- 20230806 more-content start -->
     <div class="more-content" style="margin-top: 20px;border-top:1px dashed #6c757d">
        <blockquote>
            <p>📣 首页篇幅有限<br>🏃 更多内容请移步「<a href="{{ "archives/" | relURL }}">文章列表</a>」查看</p>
        </blockquote>
     </div> 
    <!-- 20230806 more-content start -->
</div>

其他的如添加统计、评论等功能,@GuangzhengLi 已经写的比较详细,根据文档查阅即可,基本上一步到位。最后把 sitemap.xml 等搜索引擎相关的文件都一一验证,然后部署上线了。

整体感受

看起做这些事情比较「花哨」,毕竟真的来看我博客的人很少,但是做这件,不仅能让自己身心愉悦,还能了解一些新的事物,何乐而不为呢?最后补一下博客所用的的产品,除了「域名」和「图床」是需要付费的,其余均免费。

博客涉及到的产品
博客涉及到的产品