更新:

  • 2018-05-26:主题换成 Even,补充多处内容。
  • 2018-02-11:主题换成 Kiera。

自己搭博客的N个理由

  • 你的笔记非常值钱,丢了就等于丢了N年工作经验。
  • 大多数博客网站都满足不了你:
    • 倒闭/停止服务/被删库时,你多半没备份。
    • 格式互不兼容,导入导出困难,复制粘贴会乱。
    • 富文本编辑器极其难用。
    • 可配置的地方不够,你讨厌的功能一堆。
  • 可以使用 Markdown 写博文:
    • 任何一个文本编辑器都能写,随时随地。
    • 足够沉浸,让你忘记格式,专注内容。
    • 备份容易:
      • GitHub, GitBook, 有道云笔记,CSDN,博客园,简书等都支持,到处复制粘贴也能得到大致相同的格式。
      • 纯文本本身也有足够可读性,网站/软件不支持也无所谓。
  • GitHub 提供免费空间和 CDN,站点放 GitHub 仓库也方便做版本控制。
    • 可以当有道云笔记的图片空间用,弥补 Markdown 笔记无法贴图的缺点。
  • 可以作为学习新技术的试验田。

准备工作

注册 GitHub

先注册一个全球最大的同性交友网站 GitHub 账号。

  • 用户名会成为网址的一部分,最好全英文小写,没空格,不太长。
    • 例:用户名 tinycedar,对应网址 tinycedar.github.io

安装Git

Mac

brew install git

PC

下载安装 Git ,全用默认设置,然后打开 Git 终端。

  • 默认工作目录是 Git 安装目录下的 mingw64 文件夹。
  • 路径分隔符使用 /\\

设置 Git 默认用户信息

git config --global user.name "tinycedar"
git config --global user.email "keith.mork@gmail.com"

# 使用 GitHub 注册的用户名和邮箱

这台机器上的每个项目提交时默认都会用这些信息。


创建 GitHub Pages

在 GitHub 上新建仓库(repository):

  • 名字必须username.github.io ,如:tinycedar.github.io

只要往里面提交一个 index.html 文件,就能通过 https://tinycedar.github.io 访问了。

不过除了前端,没几个人愿意碰 HTML 和 CSS,这么建站实在太2000年代了,生成器用起来。


使用静态网站生成器搭建站点

个人网站/博客通常只需要简单的静态页面,这类工具 正好可以把 Markdown 文档转换成网页,非常方便。

这里选择 Hugo,特点是生成速度非常快(构建5000篇文章不用10秒),安装配置也不算复杂,适合不想折腾的人。

它最实用的就是实时预览功能,不需要发布到 Github 就能看到改动。

安装

Mac

brew install hugo

# 查看版本
hugo version

# 查看帮助
man hugo
hugo help

PC

下载后解压,把 hugo.exe 的路径加进环境变量 Path 就行。


新建站点

假设站点文件想放在当前目录的 hugo-blog/ 目录下:

hugo new site -f yaml hugo-blog

# -f, --format {yaml|json|toml}    指定 config 和 front matter 的格式,默认 toml。
# 推荐 yaml 格式。

生成的目录结构如下:

$ cd hugo-blog
$ tree
.
├── archetypes  # 博文模板
│   └── default.md  # 默认模板文件
├── config.yaml  # 主配置文件
├── content  # 存放博文,里面的子目录叫做 section。
├── data  # 生成网站时的配置
├── layouts  # 网页框架,放在这里的文件会比主题里的同名文件优先,可以在不动主题源码的情况下覆盖部分设置。
├── static  # 静态资源,这目录下的文件会原封不动的拷到站点根目录下。
└── themes  # 主题

https://gohugo.io/commands/hugo_new_site/
https://gohugo.io/getting-started/directory-structure/


下载主题

默认不带主题,从 这里 挑喜欢的下载。

推荐 CR(黑白极简)风格的主题,我用的是 Steam Kiera Even

git clone https://github.com/olOwOlo/hugo-theme-even themes/even

修改配置文件

编辑 config.yaml,默认值如下:

baseURL: http://example.org/  # 替换为你的网址
languageCode: en-us  # 中文博客建议改为 zh-cn。如果文章内容不是这里指定的编码,Chrome 会弹出要不要翻译页面的提示。
title: My New Hugo Site  # 替换为你的博客标题

【重要】建议在配置文件里指定主题,否则每次构建都要通过命令行传參。

如果配置文件和命令行都不指定主题,打开网站只能看到一片空白。

theme: even

常用设置:

googleAnalytics: XXX  # Google Analytics ID
# 多数国外主题只支持 Google 的站点统计,Even 作为国人开发的主题,还支持不蒜子和百度统计。

defaultContentLanguage: zh-cn  # 默认 en,文章内容的默认编码,必须用 "-" 和小写。有些主题支持多语言,这里的设置会改变菜单和按钮的显示语言。
#defaultContentLanguageInSubdir: false  # 如果站点支持多语言,这选项为 true 时,/ 会重定向到对应的语言版本,如 /en/、/zh-cn/ 等。
hasCJKLanguage: true  # 文章内容是否有中日韩文,会影响字数统计。

enableEmoji: true  # 支持表情
enableGitInfo: true  # 开启后可以用 Git 提交时间作为文章最后修改时间。
enableRobotsTXT: true  # 自动生成给搜索引擎爬虫用的 robots.txt。如果你打算手写,可以不启用。
# https://gohugo.io/templates/robots/

metaDataFormat: yaml  # 文章开头的元数据(Hugo 称为 front matter)的格式
preserveTaxonomyNames: true  # 标签名是西欧字母时保留原文,不自动转为对应的英文字母

paginate: 10  # 分页长度,每页显示多少篇博文

# 元数据设置,各种日期的匹配规则。从数组第1个元素开始,匹配不上才到下一个。
frontmatter:
  date: ['date', ':filename', ':default']  # 名为 date 的变量、文件名开头 yyyy-mm-dd- 的前缀、默认
  lastmod: [':git', ':fileModTime', ':default']  # Git 提交时间、文件属性里的修改时间、默认


# https://gohugo.io/content-management/urls/#canonicalization
disablePathToLower: true  # URL 路径区分大小写
permalinks:
  post: /:sections/:year/:month/:day/:slug

# URL 格式,每个 key 对应 content/ 下的一级子目录。
# :sections 代表 content/ 下的目录结构,原样映射成 URL 路径。
# :year、:month、:day 都会从文件前缀 yyyy-mm-dd 里提取。
# :slug 在文章的 front matter 里定义,如果没定义会用文件名。

其他主题常用,但 Even 不需要的设置:

disqusShortname: XXX  # Disqus 用户名
# 静态网站只能靠第三方插件实现评论功能,很多国外主题使用 Disqus,但国内访问有点慢,有时加载不出来。
# Even 支持 gitment,用 GitHub issue 当评论,不需要这个。


# 很多主题都使用默认的 Pygments 做语法高亮,设置如下。
# 颜色比较丑而且行号从来对不齐,这也是我放弃 Kiera 转向 Even 的原因之一。
# Even 用 highlight.js 做语法高亮,效果跟 Pygments 完全没得比。

# https://gohugo.io/content-management/syntax-highlighting/
pygmentsCodeFences: true
pygmentsCodeFencesGuessSyntax: true
# https://help.farbox.com/pygments.html
pygmentsStyle: friendly  # {monokai | friendly | vs | autumn | ...}
#pygmentsOptions: "linenos=inline"  # {inline | table}

除了 Hugo 本身的参数,每个主题都有自己特有的参数,请参考相应主题的官方文档。

Even 可以看看官方的 配置文件示例

https://gohugo.io/getting-started/configuration/


编辑默认模板

archetype/ 下新建 .md 文件,如果文件名跟新建文章时的 SECTIONNAME 一致,则该 section 下的文章都会套用这模板。

找不到匹配的 section 才会套用 default.md

default.md 的默认 front matter:

---
title: '{{ replace .Name "-" " " | title }}'
date: {{ .Date }}
draft: true
---

添加常用 front matter(加在分隔符之间):

slug: {{ .TranslationBaseName }}  # URL 路径的最后一部分
# .TranslationBaseName 为不带语言标识符的文件名。比如文件名为 foo.en.md,得到 foo
tags: []
categories: []

另外 Even 主题还配置了一些特有的参数,参见它的 默认模板

https://gohugo.io/content-management/archetypes/
https://gohugo.io/variables/files/


调整样式

如果对主题某些地方不满意,又不想直接改主题源文件,可以去 themes/<主题名>/layouts/ 看看都有什么文件,把要改的拷到 layouts/ 下。

文件查找顺序大致如下,同名文件在 layouts/ 下的比主题里的优先,匹配到 section 名的比 _default 优先。

layouts/<section>/...
themes/<主题名>/layouts/<section>/...
layouts/_default/...
themes/<主题名>/layouts/_default/...

https://gohugo.io/templates/lookup-order/#examples-layout-lookup-for-regular-pages


写博客

新建博文

hugo new post/2016-07-19-first.md

# 格式:<SECTIONNAME>/<FILENAME>.<FORMAT>

可以看到在 content/ 下生成了 post/ 目录,post/ 下有 2016-07-19-first.md 文件。

推荐文件名加上日期前缀,因为通常所有博文都放在同一个目录下(这样配置最简单),带日期一眼就能区分新旧文章。

这操作经常用,建议写成脚本:(例如叫做 new

  • 自动加上当天日期做前缀
  • section 名默认叫 post
  • 因为上面在配置文件里已经设置了把日期前缀提取到 URL 路径,front matter 里的 slug 去掉前缀。

    #!/bin/bash
    
    [[ -z "$1" ]] && echo "Usage: $(basename "$0") FILENAME(without suffix) [SECTIONNAME]" && exit 1
    section_name=${2:-post}
    
    date_prefix=$(date +%Y-%m-%d-)
    file_path="${section_name}/${date_prefix}$1.md"
    hugo new "${file_path}"
    
    sed -i '.bak' "s/slug: ${date_prefix}/slug: /" "content/${file_path}"
    rm -f "content/${file_path}.bak"
    
    # Mac 的 sed -i 的第1个参数必须为备份文件后缀名

编辑博文

用文本编辑器打开刚才生成的文件,可以看见类似如下内容(YAML格式):

---
title: "2016 07 19 First"
date: 2018-02-10T23:16:02+08:00
draft: true
---

(或默认的 TOML 格式):

+++
date = "2016-07-19T00:12:34+08:00"
draft = true
title = "2016 07 19 first"

+++

像这样的出现在每篇文章前的元数据叫 front matter--- 之间包着的内容会解析为 YAML,+++ 之间包着的内容会解析为 TOML。

在下面的空白处用 Markdown 格式写正文,如:

## Hello Hugo

坚持写博客的好处:

- 记录心得
- 整理思路
- 分享交流
- 求职展示

完整文件见这里,渲染成 HTML 的效果见 这里

https://gohugo.io/content-management/front-matter/


资源文件

放到 static/ 下,这目录下的所有文件和目录都会原封不动的拷到站点根目录下。

我习惯用的目录结构:

.
├── CNAME
├── css  # CSS 文件目录
├── googleXXX.html  # Google Analytics 的验证网页
├── img  # 图片目录
│   ├── reward  # 特定的主题专门建目录
│   │   ├── alipay.jpg  # 支付宝收款二维码
│   │   └── wechat.png  # 微信赞赏二维码
│   └── post  # 一般的博文配图按年月日建目录
│       └── 2018
│           └── 02
│              └── 28
├── js  # JS 脚本目录
└── robots.txt

【注意】

  • 博文里引用图片时要写完整 URL,前面是实际的域名,后面是相对 static/ 目录的路径。
    • 例:![微信打赏](http://tinycedar.com/img/wechat_reward_qrcode.png)
  • Hugo 不会帮你压缩图片,也不提供缩略图,建议提交前用工具处理图片。
  • 不重要的图可以压成 70% jpg,宽度缩到 600px。颜色少可以试试 8色 png。
  • 命令行工具有 imagemagick 用来裁剪和压缩图片,转格式等,exiftool 用来去掉 EXIF 信息。

本地预览

运行 hugo server,就会启动一个很简单的 HTTP 服务器。浏览器打开 localhost:1313 就能看到生成的网页,样式跟发布到线上完全相同。

之后文件有任何改动都会自动重新构建和刷新浏览器页面。

【注意】

  • 运行 hugo server 时,当前工作目录必须为站点根目录(包含配置文件),否则提示 Error: Unable to locate Config file.
  • Hugo 每次生成站点时不会删旧文件,因此推荐把预览和发布目录分开,并且每次生成前把旧目录删除。

这操作经常用,建议写成脚本:(例如叫做 dev_preview

#!/bin/bash

[[ -d dev ]] && rm -rf dev
hugo server --buildDrafts --destination dev --disableFastRender

# -D, --buildDrafts[=false]    文章的默认状态是草稿,草稿默认不会构建,必须加上这参数才会生成页面。
# -w, --watch    文件有改动时自动重新构建并刷新浏览器页面。(默认就带这个,不传也行)
# -d, --destination DIR    输出目录。不传这参数的话构建出来的文件只会放在内存里。
# --disableFastRender    有改动时触发完整构建。反正 Hugo 非常快,几百毫秒根本感觉不到。

ctrl + c 结束 hugo server 。


生成发布页面

首先把要发布的文章的 draft 属性改为 false,运行 hugo

执行后会生成 public/ 目录,可以看到之前的 Markdown 文件转换成了文件夹 + HTML 文件。

这操作经常用,建议写成脚本:(例如叫做 release

#!/bin/bash

if [[ -d public ]]; then
    GLOBIGNORE=*.git
    rm -rf -v public/*
fi

hugo

if [[ -n "$1" ]]; then
    cd public
    git add -A
    git commit -m "$1"
    git push
fi

再用 hugo server 检查一下有没有 draft 忘了改,确定文章能看到就可以发布了。

这步也可以写成脚本:(例如叫做 preview

#!/bin/bash

hugo server --disableFastRender

本地图片由于还没上传,肯定全是叉。介意的话可以先把图传上去,验证过路径都写对了才发布网页。


发布到 GitHub

可以装 GitHub Desktop 或 SourceTree 等客户端,或者直接命令行:

cd public
git init
git remote add origin "git@github.com:tinycedar/tinycedar.github.io.git"  # 替换成你的 GitHub Pages 仓库地址
git add -A
git commit -m "first commit"
git pull
git push -u origin master

# 注意:要用 SSH 的仓库地址才能用公钥,如果用了 HTTPS 的仓库地址,必须每次输用户名密码。

之后会提示输入在 GitHub 注册的邮箱和密码。

提交成功后,浏览器打开https://tinycedar.github.io,就能看到刚才的页面了。

第一次麻烦点,之后每次提交都很简单:(已经写在上面的 release 脚本里,参数传 true 就会提交和发布)

git add -A
git commit -m "XXX"
git push

使用 SSH 密钥登录 GitHub

每次发博文都输用户名密码太麻烦,用密钥代替密码就方便多了。

前提是机器只有你一个人用。

生成SSH密钥对

mkdir ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa -C "keith.mork@gmail.com"
  • 提示 Enter file in which to save the key 时直接回车,使用默认设置。
    • 文件名不用改,GitHub 连接时只认 id_rsa
  • 提示 Enter passphraseEnter same passphrase again 时,可以直接回车,不使用口令(否则每次提交时会要求输入口令)。

https://help.github.com/articles/generating-an-ssh-key/


添加 SSH 公钥到 GitHub

  1. 复制公钥文件(默认 ~/.ssh/id_rsa.pub)的内容。
  2. 登上 GitHub,在个人设置里找到 SSH and GPG keys,新建 SSH key,粘贴进去。
  3. 取个容易识别的名字,如 Mac-Home PC-Work 等,保存。
  4. 测试是否成功:

    ssh -T git@github.com  # 用户名就是git,不用改。
    # 如果报错,这样看详细信息:
    ssh -vT git@github.com
    
    # 看到以下讯息就是成功了:(虽然命令返回 1)
    # You've successfully authenticated, but GitHub does not provide shell access.

看到 Are you sure you want to continue connecting 时输 yes 回车,然后就可以了。

如果依然每次都问用户名密码,可能是当初加仓库地址时用了 HTTPS 格式,改为 SSH 格式的地址就好了:

git remote set-url origin git@github.com:tinycedar/tinycedar.github.io.git

发布 Hugo 工程源文件到 GitHub

源文件比生成的发布文件重要得多,必须备份到 GitHub。发布文件丢了随时重新生成,源文件丢了就没了。

  • 在 GitHub 新建仓库,例如叫 hugo-blog,不要勾选创建 README.md 。
  • 在工程根目录下创建 .gitignore 文件,写上不需要备份的目录和文件,例:

    public
    themes
    dev
    .git
    .DS_Store
    *.bak
    *_bak
    *.old
    *.log
  • 如果想写项目简介,创建 README.md 文件。

  • 和之前类似的操作:

    git init
    git remote add origin "git@github.com:tinycedar/hugo-blog.git"  # 替换成你的 GitHub 仓库地址
    git add -A
    git commit -m "first commit"
    git pull
    git push -u origin master

如果忘了写 .gitignore,把 public 和 themes 也提交了上去,会发现它们被认为是 submodule (因为里面有 .git 目录)。即使之后设置忽略它们,每次里面的文件有更新时都会出现烦人的子模块状态变更的记录。

解决方法:先把那2个目录复制到别的地方,把它们删掉,写好 .gitignore,在 .git/config 里删掉 [submodule] 相关内容,提交,再把它们搬回来。


结束语

到这里,一个属于自己的静态博客基本成型了。

可能样式、功能或别的细节不能完全令人满意,但那些基本不影响写文章,可以先专注于输出内容,其他留到以后慢慢优化。


参考

基于GitHub Pages + Hugo构建个人博客, 2018-03
GitHub Pages (User or organization site下面有手把手教程)
http://gohugo.io/overview/usage/
使用hugo搭建个人博客站点, 2015-08
Hugo 对比 Jekyll :两大领先的静态页面生成器之间的比较, 2017-06