从Hexo到Hugo-记录博客框架切换

嘶,总算是学校的事情忙完了,终于有点时间写写博客了
这个学期感觉一直都在和时间赛跑,没啥时间写博客,自己的项目也基本都断了
当然这些都是另外的话题了

如果之前有看过我博客的人,应该会发现现在网页大变样了
现在博客的框架从Hexo换到了Hugo
中间切换花了我很多时间,里面也有很多能说道说道的
需要注意的是,我的部署方案跟官方的不一样,所以部分配置会和官方文档有所出入

<0x00> 为什么要换框架

简单来说,也算是我闲着没事干
当然,之前Hexo的流程不是很舒服,写博客的话图片很不好引用
然后的话主题也不是很好看(我没时间去改主题)

然后正好想尝试下新东西,然后就看到了Hugo,于是就试了试
尝试之后发现这个框架使用体验确实舒适很多,于是就打算换框架了

换了框架后,好处主要有以下几点:

  • 文章的图片资源可以更好的管理
  • Obsidian更好的融合
  • 舒适的预览体验

<0x01> 安装Hugo与新建站点

安装Hugo

windows为例,其他系统只能自己看资料了 安装Hugo是很简单的

如果有安装scoop的话,可以直接敲命令安装

scoop install hugo-extended

这里安装的是带扩展的版本,因为我使用的主题需要这个

如果有安装winget,也可以直接敲命令安装

winget install Hugo.Hugo.Extended

如果上面两个包管理器都没安装,那么需要从Release中下载预构建的版本
找到带windows的压缩包下载,里面会有一个hugo.exe
然后丢到什么路径,添加到windows环境变量里面,这样就可以在命令行中调用了

新建Hugo站点

完成安装后,尝试新建站点来测试是否安装成功

在希望存放站点内容的位置打开终端

hugo new site TestBlog

这样会在这个位置创建叫TestBlog的文件夹,里面会包含hugo站点的结构
以上就完成了Hugo站点的搭建

<0x02> 配置Stack主题

这里使用的主题是Stack,简洁好看

路径约定

./表示存放所有Hugo站点的路径
所以./TestBlog就是存放TestBlog站点的路径
本文后面所有的路径都将按此表示

安装Stack主题

./TestBlog/themes是存放所有主题的路径
在此打开终端

git clone https://github.com/CaiJimmy/hugo-theme-stack.git

克隆主题仓库

然后回到./TestBlog,里面有个hugo.toml
theme的值修改为'hugo-theme-stack'

配置头像

进入./TestBlog/themes/hugo-theme-stack
这里是Stack主题的文件

我们需要修改的头像在./TestBlog/themes/hugo-theme-stack/assets/img
那个avatar.png便是我们我修改的图像
找自己的头像拖进去然后改名就可以了

配置网站图标

配置网站图标稍微麻烦点
首先进入./TestBlog/themes/hugo-theme-stack
创建叫static的文件夹,里面放入网站图标,这里取名favicon.webp
然后回到./TestBlog/themes/hugo-theme-stack,打开文件config.yaml
找到favicon,将值改为favicon.webp,也就是网站图标的文件名

配置社交媒体菜单

(指的是这个东西)

./TestBlog/themes/hugo-theme-stack,打开文件config.yaml
在文件最后添加这些东西

menu:
    main: []
    social:
        - identifier: github
          name: 起个名吧
          url: GitHub链接
          params:
              icon: brand-github
        - identifier: bilibili
          name: 起个名吧
          url: bilibili链接
          params:
              icon: brand-bilibili

但仅仅这样是不行的,如果构建网页会说没有brand-bilibili.svg
所以我们需要添加这个svg
进入./TestBlog/themes/hugo-theme-stack/assets/icons,里面是所有的图标文件
新建一个文件,命名为brand-bilibili.svg,添加以下内容

<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
  stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
  class="icon icon-tabler icons-tabler-outline icon-tabler-brand-bilibili">
  <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
  <path d="M3 10a4 4 0 014-4h10a4 4 0 014 4v6a4 4 0 01-4 4H7a4 4 0 01-4-4v-6z"></path>
  <path d="M8 3l2 3"></path>
  <path d="M16 3l-2 3"></path>
  <path d="M9 13v-2"></path>
  <path d="M15 11v2"></path>
</svg>

(从别人博客薅的)
保存即可

如果要添加其他网站图标也是这样

<0x03> 文章路径与侧栏

Hugo的所有文章都是放在./TestBlog/content里面

Hugo支持页面包来对文章资源进行管理
页面包就是按路径对文章进行打包,一个文章只能访问自己页面包内的内容
这样就将每个文章独立开来,不像之前Hexo所有图片都在一个文件夹中
(或许Hexo也有办法实现,但我不知道)

修改默认文章目录

Stack主题默认指定./TestBlog/content/post为文章目录
这里我改成了./TestBlog/content/article

./TestBlog/themes/hugo-theme-stack,打开文件config.yaml
找到mainSections将值改为article即可

侧栏部分

这里的配置建议参考Stack主题给的exampleSite配置
直接按page里面的结构配置即可

文章路径参考

我这的文章路径是这样的

content
├── article
|   ├── posts
|   |   ├── 随便起的文章名字
|   |   |   ├── picture
|   |   |   |   └── 1.png
|   |   |   └── index.md
|   |   ├── ...
|   |   └── ...
|   └── categories
|       ├── Blog
|       |   ├── 随便起的文章名字
|       |   |   ├── picture
|       |   |   |   └── 1.png
|       |   |   └── index.md
|       |   ├── ...
|       |   └── ...
|       ├── ...
|       └── ...
├── page
|   ├── about
|   |   └── index.md
|   ├── archives
|   |   └── index.md
|   ├── links
|   |   └── index.md
|   └── search
|       └── index.md
└── _index.md

<0x04> obsidian配置

用obsidian在./TestBlog/content创建仓库
obsidian里面需要配置的东西不多,主要是把链接设置为相对文件的路径

这样配置就可以了

由于使用页面包进行管理,所以所有的文章文件名都是index.md
这个会导致obsidian节点视图之类的功能显示的都是index,我目前没有很好的办法

<0x05> 部署到Github Page上

虽然官方文档有讲,但他们采用的是将整个站点push到github上
然后采用Github Action来在上面构建网页
这样的话如果Action脚本有误可能导致github page行为和本地测试不一样

我打算本地构建,然后将构建好的网页push到github上面,这样逻辑跟Hexo那套差不多
但Hugo没有这样的自动部署配置,所以要自己来
大致流程是在./TestBlog/public中初始化git仓库
添加远程git仓库
拉取后提交所有更改
如果需要切换分支的话再切换分支
之后只要构建后提交更改并上传即可

具体命令参考下面的自动化脚本

<0x06> 通过PowerShell实现自动化

因为是在windows平台,所以这里采用PowerShell来实现一些自动化

统一变量配置

因为会有好几个脚本,所以需要一个统一的变量配置

# configs.ps1
# 获取脚本文件夹路径
$global:ShellScriptDir = Get-Location

# 主目录
Set-Location ..
$global:SiteDir = Get-Location
Set-Location $global:ShellScriptDir
# 内容目录
$global:ContentDir = Join-Path -Path $global:SiteDir -ChildPath "content"
# 文章目录
$global:ArticleDir = Join-Path -Path $global:ContentDir -ChildPath "article"
# 一般文章目录
$global:PostsDir = Join-Path -Path $global:ArticleDir -ChildPath "posts"
# 分类文章目录
$global:CategoriesDir = Join-Path -Path $global:ArticleDir -ChildPath "categories"

# 基础URL
$global:BaseURL = "[...]"
# 远程Git仓库链接
$global:RemoteGitURL = "[...]"
# 目标分支
$global:TargetBranch = "[...]"

# 编辑器路径
$global:EditorPath = "[...]"

创建文章

因为希望每次创建文章都会自动创建对应的页面包,所以也需要一个脚本

# NewPost.ps1
param(
    [string]
    [ValidateNotNullOrEmpty()]
    $PostName,
    [string]
    $CategoryName,
    [bool]
    $IsStartServer = $false,
    [bool]
    $IsCreatePictureFolder = $false
)

function ValidatedName {
    param (
        [string]
        [ValidateNotNullOrEmpty()]
        $Name
    )
    # 其他的特殊字符
    $SpecialCharacters = [char[]]@(
        "` "
        "`!"
        "`$"
        "`%"
        "`&"
        "`'"
        "("
        ")"
        "*"
        ","
        "-"
        "/"
        ":"
        ";"
        "<"
        "="
        ">"
        "?"
        "@"
        "["
        "]"
        "^"
        "_"
        "``"
        "{"
        "|"
        "}"
        "~"
    )
    # 因为我会写C#相关的文章,#有一些意义,对#做特殊替换
    $Name = $Name.Replace("`#", "Sharp")
    # 先每个替换成-
    foreach ($Character in $SpecialCharacters) {
        $Name = $Name.Replace($Character, "-")
    }
    # 返回时使用正则表达式匹配连续的-,并替换为单个-
    return $Name -Replace '([-])+', '-'
}

# 保存当前的工作路径
$localDir = Get-Location
# 导入配置变量
. .\configs.ps1
# 切换工作路径
Set-Location $global:SiteDir
# 合法化路径名,不然图片会出问题
$validPath = ValidatedName -Name $PostName

# 普通文章创建
if ($CategoryName -eq "") {
    # 新建Hugo文章
    $newPostPath = Join-Path -Path "content\article\posts" -ChildPath (Get-Date -Format "yyyy")
    $newPostPath = Join-Path -Path (Join-Path -Path $newPostPath -ChildPath $validPath) -ChildPath "index.md"
    hugo new $newPostPath
    # 生成图片文件夹路径
    $newPostPicture = Join-Path -Path (Join-Path -Path $global:PostsDir -ChildPath $validPath) -ChildPath "picture"
}
# 需要分类的文章
else {
    # 新建Hugo文章
    $categoryPath = Join-Path -Path "content\article\categories" -ChildPath $CategoryName
    $newPostPath = Join-Path -Path (Join-Path -Path $categoryPath -ChildPath $validPath) -ChildPath "index.md"
    hugo new $newPostPath
    # 生成图片文件夹路径
    $categoryPath = Join-Path -Path $global:CategoriesDir -ChildPath $CategoryName
    $newPostPicture = Join-Path -Path (Join-Path -Path $categoryPath -ChildPath $validPath) -ChildPath "picture"
}
# 需要创建图片文件夹的话就创建
if ($IsCreatePictureFolder) {
    mkdir $newPostPicture
}
# 启动编辑器
# Start-Process $global:EditorPath
# 是否开启服务器预览
if ($IsStartServer) {
    hugo server
}
# 返回之前的工作路径
Set-Location $localDir

使用示例

# 创建一篇普通的文章
.\NewPost.ps1 -PostName 从Hexo到Hugo-记录博客框架切换
# 创建一篇文章,并分类为Blog
.\NewPost.ps1 -PostName 从Hexo到Hugo-记录博客框架切换 -CategoryName Blog

运行后会创建对应的页面包

发布

这里涉及到两个文件,一个InitGit.ps1用来初始化git仓库,一个Deploy.ps1做推送

InitGit.ps1如下

# InitGit.ps1
param(
    [bool]
    $IsForcePush = $false
)

# 保存当前的工作路径
$localDir = Get-Location
# 导入配置变量
. .\configs.ps1
# 切换工作路径
Set-Location (Join-Path -Path $global:SiteDir -ChildPath "public")

# 初始化git
git init
git remote add origin $global:RemoteGitURL
git pull
git add .
git commit -m ("Init: " + (Get-Date -Format "yyMMdd-HH:mm"))
git branch -u $global:TargetBranch
if ($IsForcePush) {
    git push origin HEAD:$global:TargetBranch --force
}

# 返回之前的工作路径
Set-Location $localDir

Deploy.ps1如下

# Deploy.ps1
param(
    [bool]
    $IsForcePush = $false,
    [bool]
    $IsClearDeploy = $false
)

# 保存当前的工作路径
$localDir = Get-Location
# 导入配置变量
. .\configs.ps1
# 切换工作路径
Set-Location $global:SiteDir
# 清除Hugo构建缓存
if ($IsClearDeploy) {
    # 递归删除所有文件
    Remove-Item -Path (Join-Path -Path $global:PublicDir -ChildPath "*") -Recurse
}
# 构建网页
hugo --gc --minify --baseURL $global:BaseURL
# 准备推送
Set-Location $global:PublicDir
# 没有初始化git的化就先初始化并强制推送
if (-not (Test-Path ".git")) {
    . (Join-Path -Path $global:ShellScriptDir -ChildPath "InitGit.ps1") -IsForcePush $true
}
else {
    git add .
    git commit -m ("Deploy: " + (Get-Date -Format "yyMMdd-HH:mm"))
    # 是否强制推送
    if ($IsForcePush) {
        git push origin HEAD:$global:TargetBranch --force
    }
    else {
        git push origin HEAD:$global:TargetBranch
    }
}

# 返回之前的工作路径
Set-Location $localDir

使用示例

# 正常的发布
.\Deploy.ps1
# 清空所有构建缓存发布
.\Deploy.ps1 -IsClearDeploy

运行即可推送到GitHub Page上

批量创建页面包路径结构

因为之前使用的是hexo,里面都是单独的markdown文件
如果自己手动一个一个移动并创建picture文件夹那就太累了
所以还是要写一个脚本完成自动化

# TransFromSingleFile.ps1
param(
    [string]
    [ValidateNotNullOrEmpty()]
    $postDir
)

# 排除文件名单
$excludeList = "_index"

Get-ChildItem -Path $postDir -File | ForEach-Object {
    if ($_.BaseName -notin $excludeList) {
        # 创建文件夹并移动
        $destDir = Join-Path -Path $postDir -ChildPath $_.BaseName
        New-Item -ItemType Directory -Force -Path $destDir
        Move-Item -Path $_.FullName -Destination (Join-Path -Path $destDir -ChildPath "index$($_.Extension)")
        # 创建对应的picture文件夹
        $newPostPicture = Join-Path -Path $destDir -ChildPath "picture"
        mkdir $newPostPicture
    }
}

使用示例

.\TransFromSingleFile.ps1 [某个路径]

运行可以为路径内所有文件创建同名文件夹,并将文件移动至对应文件夹后重命名为index.md
里面也会创建picture文件夹

Licensed under CC BY-NC-SA 4.0