基于 Hugo + Org Mode + Github Page 的静态站点搭建
Table of Contents
简介
现在已经全面转战 Emacs 和 Org Mode 了,想搭个人博客,折腾了很久。这里记录搭建思路和过程,供参考。
工具
- Emacs + Org Mode
- Hugo 静态站点生成制作
- ox-hugo 将 org 文件转换成 md 文件
- GitHub Page 博客托管平台
- Gitalk 博客评论,基于 GitHub 的 Issue
搭建流程
假定已经安装了 Emacs 和 Git 。
安装 Hugo
官方的下载页面在这里 https://gohugo.io/getting-started/installing ,根据自己的系统去安装即可。
sudo pacman -S hugo
Github Page 配置
首先,新建一个仓库,仓库名必须是 <Name>.github.io
, <Name>
是的 github 的昵称。
如果新建时选了自动建 README.md
,就好了。如果没有执行(里面的 Name
是 github 昵称):
# 随便一个 REMAD.md
echo "# Just a README" >> README.md
git init
git add README.md
git commit -m "first commit"
# 新基础分支
git branch -M main
git remote add origin [email protected]:Name/Name.github.io.git
# 提交到基础分支
git push -u origin main
在 Settings
里边,在左边那里点开 Page
。
至此 GitHub Page 就搭建好了,等几分钟它那边部署,然后就可以去访问 https://<Name>.github.io/
了(<Name> 是 github 昵称),不过现在应该是 404 ,里面什么东西都还没有。
Hugo 使用
新建项目, blog
是项目目录。
hugo new site blog
然后会生成如下的项目:
├── blog │ ├── archetypes/ │ ├── config.toml │ ├── content/ │ ├── data/ │ ├── layouts/ │ ├── public/ │ ├── static/ │ ├── themes/
具体作用如下[1]:
archetypes
, 内容模板,可以取参考里面看,暂时不用管。config.toml
, 配置文件,详见我的 Hugo 配置content
, 内容目录,用于存放 md 源文件。data
, 数据模板目录,暂时不用管。layouts
, HTML 模板目录,md 生成 HTML 的模板,有一套单独的语言描述。暂时也不用配。public
, 发布目录,生成的 HTML 会存到这个目录下边。后面需要修改。themes
, 主题目录,用于存放主题相关配置。
测试时,进入 blog
目录,使用:
hugo server
然后会开启默认端口,从输出信息中可以看到。从浏览器访问就能看到。
项目目录修改
克隆仓库:
# blog 下
git clone [email protected]:<Name>/<Name>.github.io.git project
在 blog/project
目录下新目录存放 org 源码的目录(手动建):
├── project │ ├── assets/ │ ├── posts/
org 文件就放在 blog/project/posts
下,静态资源就放在 blog/project/assets
下。
因为 Github Page 只能选 /
根目录或者 /docs
目录作为站点目录,所以需要在配置中把站点输出目录修改一下。在我的 Hugo 配置那里我会说的。
Hugo 主题
主题可以去官网上找 https://themes.gohugo.io/ ,我参考的是前辈用的 LoveIt [2],功能强大,界面美观,且不会太花哨。
先找到主题的 github 仓库 https://github.com/dillonzq/LoveIt,然后把它作为 hugo 项目的子模块
# 在 blog 目录下操作
git submodule add https://github.com/dillonzq/LoveIt.git themes/LoveIt
或者直接克隆也可以。
复制之后直接贴到 config.toml
即可。
我的 Hugo 配置
配置文件可以说三种类型: toml
, yaml
和 json
,我用的是 yaml
,简洁;把 LoveIt 官网给的完整配置转成 yaml
,然后手动调整,参考如下。
因为配置里面可能会用到密钥什么的,因此不建议公开到 GitHub 上边,所以只上传 Org 源码, 静态文件和站点文件。如下所示:
├── project │ ├── assets/ # 存放静态文件 │ ├── posts/ # 存放 org │ ├── docs/ # 站点文件
在 GitHub Page 的设置中把分支那里的目录设置成 /docs
(只能是 /(root)
或 /docs
),配置文件中修改输出目录 publishDir
为 project/docs
,以适配 Github Page , public
目录就可以不要了,我把这个设置放到了第一行。
这样源码 org 文件和站点代码都可以被托管到 GitHub 上。
config.yaml
publishDir: project/docs
# 站点URL
baseURL: https://fingerknight.github.io/
# 默认语言
defaultContentLanguage: zh-cn
languageCode: zh-CN
hasCJKLanguage: true
# 主题
theme: LoveIt
# 网站标题
title: 手指骑士的病房
# 作者
author:
name: Finger Knight
email: [email protected]
link: https://fingerknight.github.io/about
# 默认每页列表显示的文章数目
paginate: 12
# 谷歌分析代号 [UA-XXXXXXXX-X]
googleAnalytics: ""
# 版权描述,仅仅用于 SEO
copyright: ""
# 是否使用 robots.txt
enableRobotsTXT: true
# 是否使用 git 信息
enableGitInfo: false
# 是否使用 emoji 代码
enableEmoji: true
# 忽略一些构建错误
ignoreErrors: ["error-remote-getjson", "error-missing-instagram-accesstoken"]
# 标记语言设置
markup:
highlight:
noClasses: false
codeFences: true
guessSyntax: true
lineNos: true
lineNumbersInTable: true
goldmark:
extensions:
definitionList: true
footnote: true
linkify: true
strikethrough: true
table: true
taskList: true
typographer: true
renderer:
# 是否在文档中直接使用 HTML 标签
unsafe: true
# 目录
tableOfContents:
startLevel: 2
endLevel: 6
# 网站地图配置
sitemap:
changefreq: weekly
filename: sitemap.xml
priority: 0.5
# Permalinks 配置
Permalinks:
posts: ':filename'
# 输出设置
outputs:
home:
- HTML
- JSON
page:
- HTML
- MarkDown
section:
- HTML
taxonomy:
- HTML
taxonomyTerm:
- HTML
# 语言设置
language:
zh-cn:
contentDir: content
languageName: 中文
# 菜单设置
menus:
main:
- identifier: main
pre: <i class="fa-solid fa-house"></i>
post: ''
name:
url: /
title: 主页
weight: 1
- identifier: tags
pre: <i class="fa-solid fa-tags"></i>
post: ''
name: ''
url: /tags/
title: 标签
weight: 2
- identifier: posts
pre: <i class="fa-solid fa-layer-group"></i>
post: ''
name: ''
url: /posts/
title: 归档
weight: 3
- identifier: about
pre: <i class="fa-solid fa-bed-pulse"></i>
post: ''
name: ''
url: /about
title: 病历
weight: 4
# 参数设置
params:
# 网站默认主题样式 ["auto", "light", "dark"]
defaultTheme: "auto"
# 哪种哈希函数用来 SRI, 为空时表示不使用 SRI
# ["sha256", "sha384", "sha512", "md5"]
fingerprint: "sha256"
# 日期格式
dateFormat: "2006-01-02"
# 网站标题, 用于 Open Graph 和 Twitter Cards
title: "手指骑士的病房"
# 网站描述, 用于 RSS, SEO, Open Graph 和 Twitter Cards
description: "痴人说梦"
# 网站图片, 用于 Open Graph 和 Twitter Cards
# images:
# - /logo.png
# 首部元素
header:
# 桌面端导航栏模式 ["fixed", "normal", "auto"]
desktopMode: fixed
# 移动端导航栏模式 ["fixed", "normal", "auto"]
mobileMode: auto
title:
# logo: /avatar.png
name: 手指骑士的病房
pre: ''
post: ''
typeit: false
# 尾部元素
footer:
enable: true
custom: ''
hugo: true
copyright: true
# 是否显示作者
author: true
# 网站创立年份
since: 2022
icp: ''
# 许可协议信息 (支持 HTML 格式)
license: <a rel="license external nofollow noopener noreffer" href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">CC BY-NC 4.0</a>
# Section (所有文章) 页面配置
section:
# section 页面每页显示文章数量
paginate: 20
# 日期格式 (月和日)
dateFormat: 01-02
# RSS 文章数目
rss: 10
# List (目录或标签) 页面配置
list:
paginate: 20
dateFormat: 01-02
rss: 10
# 主页配置
home:
# RSS 文章数目
rss: 10
# 主页个人信息
profile:
enable: true
# gravatarEmail: ''
avatarURL: /img/YunRuAvatar.jpg
title: ''
subtitle: 痴人说梦
typeit: false
# 是否显示社交账号
social: false
# 免责声明
disclaimer: ''
# 主页文章列表
posts:
enable: true
paginate: 5
defaultHiddenFromHomePage: false
# 文章页面全局配置
page:
# 是否在主页隐藏一篇文章
hiddenFromHomePage: false
# 是否在搜索结果中隐藏一篇文章
hiddenFromSearch: false
# 是否使用 fontawesome 扩展语法
fontawesome: true
# 是否在 RSS 中显示全文内容
rssFullText: false
prev: false
next: false
linktomarkdown: false
# 开启上下篇
nav: false
# 目录
toc:
enable: true
# 是否保持使用文章前面的静态目录
keepStatic: false
# 是否侧边目录自动折叠展开
auto: false
# 代码
code:
# 是否显示代码块的复制按钮
copy: true
# 默认展开显示的代码行数
maxShownLines: 50
# KaTeX 数学公式
math:
enable: true
inlineLeftDelimiter: ''
inlineRightDelimiter: ''
blockLeftDelimiter: ''
blockRightDelimiter: ''
copyTex: true
mhchem: true
# 地图
mapbox:
accessToken: ''
lightStyle: 'mapbox://styles/mapbox/light-v10?optimize=true'
darkStyle: 'mapbox://styles/mapbox/dark-v10?optimize=true'
navigation: true
geolocate: true
scale: true
fullscreen: true
# 关闭分享
share:
enable: false
# 评论看后面
# comment:
# enable: true
# gitalk:
# 第三方静态资源
library:
css: {}
js: {}
# 页面 SEO 配置
seo:
images: []
publisher:
name: ''
logoUrl: ''
# TypeIt 配置
typeit:
speed: 100
cursorSpeed: 1000
cursorChar: '|'
duration: -1
# 网站 SEO 配置
seo:
image: ''
thumbnailUrl: ''
# 第三方库文件的 CDN 设置
# CDN 数据文件名称, 默认不启用
# ["jsdelivr.yml"]
# 位于 "themes/LoveIt/assets/data/cdn/" 目录
# 可以在你的项目下相同路径存放你自己的数据文件:
# "assets/data/cdn/"
# cdn:
# data: ''
# 搜索设置
search:
enable: true
# 搜索引擎的类型 ["lunr", "algolia"]
type: lunr
# 文章内容最长索引长度
contentLength: 4000
# 搜索框的占位提示语
placeholder: ''
# 最大结果数目
maxResultLength: 10
# 结果内容片段长度
snippetLength: 50
# 搜索结果中高亮部分的 HTML 标签
highlightTag: em
# 是否在搜索索引中使用基于 baseURL 的绝对路径
absoluteURL: false
关于资源目录
假设有个写好了的 md ,位置 content/test.md
,那么它会出现在站点的根目录,也就是需要访问 https://yoursite.com/test.md
。
同理,静态资源是存在 static
下边的,假如你有网站图标和某个 css 在它下边, static/favicon.ico
和 static/test.css
,那么访问的时候是 https://yoursite.com/favicon.ico
和 https://yoursite.com/test.css
。
所以对于文本中出现的图片,可以新建一个目录 static/assets/
,放在里面,后续访问就是 https://yoursite.com/assets/xx
。
安装 ox-hugo
官网的安装页面:https://ox-hugo.scripter.co/#installation。
(with-eval-after-load 'ox
(require 'ox-hugo)
(setq org-hugo-base-dir ../blog/"
;; org-hugo-section "posts"
org-hugo-default-static-subdirectory-for-externals "img"
org-hugo-auto-set-lastmod t
org-hugo-export-with-toc nil))
org-hugo-base-dir
, 项目的根目录org-hugo-section
, 文档的目录,相对于contnet
而言。如果值是posts
,它的路径就是 ../blog/content/posts/~ 。这里注释了是因为默认就是posts
org-hugo-auto-set-lastmod
, 自动修改“最近更新时间”org-hugo-export-with-toc
, 是是否开启文档目录。这个目录放到文章开头, Hugo 配置里面有目录了,所以不用。org-hugo-default-static-subdirectory-for-externals
, 静态文件的目录,相对于static
而言。例如它的值是img
, 那么图片就会存在 ../blog/static/img~
ox-hugo 使用
Org file 的基础配置
#+title: 文章标题 #+date: [2022-09-10 Sat] #+hugo_tags: tags1 tags2 #+hugo_categories: Class #+hugo_draft: true
解释如下:
title
, 文章标题date
, 文章创建日期hugo_tags
, 标签。空格分隔hugo_categories
, 分类hugo_draft
, 是否为草稿,如果是的话,不会发布
对于特定文件,可以在文件的头配置里面覆盖掉基础配置。比如“关于”页面 about.org
的内容:
#+hugo_base_dir:../blog/ #+hugo_section: ./ #+hugo_auto_set_lastmod: t #+hugo_custom_front_matter: :toc false
这样就使得输出的 about.md
直接在 blog/content/
下面,覆盖了 .emacs
配置里面写的 blog/content/posts
使用命令 C-c C-e H h
,输出当前文件为 md,这是启动 hugo server
就能看到了。
设置评论
LoveIt
主题给出了很多选择,我用的是 Gitalk[3] ,它是基于 Github 的 Issue 模块设计的。它十分太巧妙,用一个 Github 仓库做评论存储 —— 并不是作为 code 来存。对于每个仓库,都有一个 Issue 区域,用来记录问题和反馈。每条 issue 下面都可以评论 —— 本质上 Issue 就是一个评论区,它在 Github 的作用就是反馈问题,用来当博客评论简直舒服(当然,评论者必须登录 GitHub 之后才能评论)。
首先建立一个 Github App,我找不到在哪里,教程里面的 url 直接跳转:Register GiHub Application.
之后可以到个人设置 - Developer Settings
- OAuth Apps
里面管理。
建好之后会生成一个 Cilent ID
和一个 Client Secrets
,记着。
然后到 Hugo 的配置里面。如果你用的是我的配置,那么文件就在 params.yaml
里面,如果用的官方单文件配置,找到对应位置即可。
找到评论这一块,关键词 comment
。 enable
设为 true
,表示开启评论。
配置 Gitalk , enable
也设为 true
,然加上几条配置。
owner
, 仓库拥有者,你的 Github 昵称。admin
, 仓库管理者,一个列表,里面也是你的 Github 昵称。repo
, 仓库名,指明要用哪个仓库的 Issue 来存评论。我用的和博客同一个仓库,方便自动化,你也可以用其他的仓库。只需要名称,我的某个仓库fingerknight/some-repo
,那么这里就填some-repo
clientId
,clientSecret
,就是刚才的提到的。
注意,这个 Github 仓库一定要是公开的 public
,不然会出现找不到。
第一次使用可能会出现如下情况:
这是因为对应的 Issue 还没建立,点 使用 Github 登录
,做一些授权就好了。此外,对于新的一篇文章,需要用你带着 Github 的 Cookie 去访问一次,它就会自动建立 Issue,否则,比如其他人看也会出现这个情况。
目前在 LoveIt 那边有提需求说应该把这个自动化,但目前 (2022-09-12) 还没有实现。
LoveIt 的 Bug
如果你有好几篇文章同时生成和发布,那么就有可能出现这个 bug ,会导致评论共用,就是 A 文章下面的评论和 B 文章下面的评论一样, A 文章下面发的评论在 B 文章那里也可以看到。
这个问题是因为二者共用了同一个 Issue ,或者说定位到了同一个 Issue 。Gitalk 是用一个 id
关键字区分不同文章的,你去你的 Issue 可以看到,每个 Issue 都有两个标签,一个是 Gitalk
,一个默认是标准格式日期(我的不是是因为我改了)。前者用来让 Gitalk 知道哪些 Issue 是用来存评论的,哪些是真的问题反馈;后者就是 id
,用来区分不同文章的。
LoveIt 的默认 id
是写死的,就是 Date
,所以如果几篇文章同时发布, Date
就是一样的,那就会定位到同一个 Issue 。
2022-09-11 我已经向项目提了一个 Bug 的 Issue ,等待后续结果。
手动改如下。在 blog/themes/LoveIt/layouts/partials/comment.html
里面边,找到 Gitalk 相关代码:
把图示中 id
后面的 .Date
给改掉。 Gitalk 官网说长度小于 50 ,参照网友的意见用 MD5 摘要,所以我改成 (md5 .File.Path)
,文章相对于 blog/content/
的相对路径的 MD5 摘要。
工作流程
至此一个博客就搭建好了,接下来说明它的工作流程。
- 编写 org 文件。把博客文章的内容写到 org 文件中,记得加头部设置。
- 格式化 org 为 md。使用命令
C-c C-e H h
格式化。 - 发布成 HTML。把所有要发布的内容格式化成 md 后,可以
hugo server
测试一下,或者直接发布,使用hugo
就可以看到project/docs
目录下生成了站点文件。 - 最后提交到 Github
git add *
git commit -m "注释"
git push
等一会儿再去访问你的 github.io 应该就好了。
自动化
如果每次都那么搞就太麻烦了。因此我写了一些东西,使得整个流程直接一步到位,可以参考。
地址:https://github.com/fingerknight/org-hugo-lazy.el,这个流程就是基于我上述的搭建过程写的,如果你是完全按照我上面的搭建 Hugo ,那么这个扩展应该很容易上手。
优化
Cloudflare Page
Cloudflare Page 也可以直接托管静态站点了,它是联动 GitHub ,把仓库的 HTML 等站点文件克隆到它的服务器然后部署。参考《几分钟、零基础搭建个人网页!- 高速直连,基于Cloudflare Page - 知乎》。
Cloudflare + DNS
我是自己注册了一个域名,然后用 Cloudflare 托管,具体可以参考我的另一篇博客《GiHub Page + Cloudflare + NameSilo 的域名配置》
后记
搞了好几天终于算可以了,留一些尾巴后面处理。
[X]
评论功能[X]
CDN 加速[X]
样式调一下,这个主题整体显得比较挤,还有字体。如果有精力把配色也改一下[ ]
还有就是 SEO 、挂友链,挤进搜索引擎,增加曝光度,这个貌似 LoveIt 已经帮忙调好了。
希望对你有帮助!