既然是写博客, 那我自然从写博客的工具, Jekyll, 开始. 我会选择 Jekyll 主要是受到 GitHub 影响. GitHub Pages 对 Jekyll 的支持使得 Jekyll 在同类中相对更容易上手.

Jekyll 总览

如果不考虑 WordPress 这种由商业公司支撑的大型架构, 常见的开源博客生成器主要是

  1. 基于 Node.js 的 Hexo;
  2. 基于 Ruby 的 Jekyll;
  3. 基于 Go 的 Hugo;
  4. 基于 React 的 Gastby;

其中后两种是基于新兴语言的新兴平台. 对于不以前端为工作的人而言, 其实哪一种平台都差不多. 这 4 种语言本来也不是在生活中会接触到的东西. 由于我一直在使用 Jekyll, 这里就只介绍 Jekyll 的使用.

本质上来讲, 选择这类工具写博客本身就是一种折腾的心理. 对我而言, 这个过程中更重要的一点是去理解网站到底是如何工作的. 观察 html, css 以及 javascript 对网页显示效果的影响比写作本身更有意思. 在 Kramdown 的加持下, Jekyll 可以很方便的进行 markdown 与 html 混排. 这让网站的制作过程在拥有直接编辑 html 的自由度的同时可以利用 markdown 的简单语法. Jekyll 让我很喜欢的一点就是它对 Liquid 的支持. 在 Liquid 的辅助下, Jekyll 的 Layout 结构可以很方便的做代码复用. 而 Kramdown 的 arttribute 功能可以在 markdown 的语法中直接修改 html 的属性.

这让我可以很容易的 hack 进 markdown 语法, 实现这个警示牌 (admonition) 功能.

这是个标题

这个功能的实现还是比较复杂的. 我一开始是仿制了一个 Liquid 写的语法分析器 (parser), 利用代码块自定义语言的功能过滤关键词, 然后替换代码块的容器.

Lemma

直到后来我发现了 Kramdonw 支持 dl, dt, dd描述列表 (description list) 元素的语法. 现在这个框写起来就更简单了, 而且去掉了语法分析器后 html 和 css 就解耦合了. 我之后想添加其他的警示框就只需要在 css 中添加了. 比如这个引理框.

不同于常见的 JSON 文件, Jekyll 使用的数据格式为 YAML.

# Example
lang_name: "ZH-CN"
title: "Q-Canon"
date_format: "%F"
list:
    - a
    - b
dict:
    name: 'Alice'
    number: 12577

YAML 在使用缩进的同时支持与 JSON 文件混排. 在因为格式混乱而不适合大型项目的同时, 它在小项目中却非常的方便.

Jekyll 的使用

Jekyll 的安装

Jekyll 最简单的使用方式就是 GitHub Pages. 直接在 GitHub 建一个公开的仓库, 然后去仓库的设置里找到 Pages. 选择一个分支和想要的主题就可以了.

pages setting

只要在仓库中添加一个 index.md 文件, 随便写点什么, 这个就是你的主页. 其他的文件只要建立一个 _post 文件夹, 然后给每一条博文新建一个 日期-名字.md 文件, 比如 2022-06-18-hello-world.md, 它就会自动被加入到博客中. 唯一需要的前置知识就是 markdown 的语法了. 每次向仓库 push 新的 commit 的时候 GitHub 会自动帮你编译, 然后部署到 https://账号名.github.io/仓库名/ 上.

当然, 这样部署的网页更新起来不是很方便, 没有办法在本地编辑的时候实时的看到效果. 想要有更好的体验最好是在本地安装 Jekyll, 等一篇博文写完了再 push 到 GitHub 上. 但对很多 Windows 用户而言, 这是一股很大的劝退力量. Ruby 对 Windows 的支持并不好, 因此 Jekyll 在 Windows 上的安装与配置会非常麻烦. 所以我们这里只以 Ubuntu 为例.

安装 Jekyll 的第一步是安装 Ruby: 用系统包管理器, 比如 apt 安装 ruby

sudo apt-get install ruby-full build-essential zlib1g-dev

这段命令是 Jekyll 官方设定的, 后两个 build-essentialzlib1g-dev 一般 Linux 都会自带, 但是使用这个命令可以确认一下, 保证安装不会出问题. 安装 ruby 的同时会安装它的包管理器 gem. 这就像 python 与 pip 的关系.

按照官方推荐, 我们需要手动设置一下 gem 的环境变量. 否则的话所有 gems 会被安装到 root 用户下.

echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc
echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc
echo 'export PATH="$HOME/gems/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

最后用 gem 安装 Jekyll 就可以了

gem install jekyll bundler

其中 bundler 是一个 gem 的依赖管理包. bundler 会用工作文件夹下的 Gemfile 文件确定需要哪些 gems.

文件夹结构

Jekyll 默认的文件夹结构如下

.
├── _config.yml
├── _data/
├── _drafts/
│   ├── begin-with-the-crazy-ideas.md
│   └── on-simplicity-in-technology.md
├── _includes/
│   ├── footer.html
│   └── header.html
├── _layouts/
│   ├── default.html
│   └── post.html
├── _posts/
│   └── 2009-04-26-barcamp.md
├── _sass/
├── _site/
├── .jekyll-cache/
└── index.html

Jekyll 中所有带下划线 _ 的文件夹或者文件都是功能性的. 在编译的时候, 这些特殊的文件以及根目录下的文件会被用于 Jekyll 编译. 其他文件, 如果是 html 或者 markdown 则会被当作一个页面处理, 剩下的所有文件会被原封不动的复制到 _site 内. 一个好习惯是保留一个不含 html 和 markdown 的文件夹, 一般命名为 assets. 这个文件夹里面储存了所有网站需要的 css, js 文件, 图片, 字体, logo 等内容. 这样可以清晰的让自己知道哪些文件在编译过程中是不会变的, 哪些文件有可能被修改转移位置.

我从上到下介绍一下这些文件夹分别是什么:

_config.yml

_config.yml 也可以被命名为 _config.yaml. 这是 Jekyll 的配置文件. 但其实这个文件里面可以什么都不写. 默认配置已经完全够用. 实际上这个文件里面写的大部分内容都是为 Liquid 服务的. 具体需要定义些什么是由模板决定的, 它实际上只是在编译中定义了一些常数.

_data/

_data/ 文件夹其实是 _config.yml 文件的延申. 我们可以在这个文件夹里面储存 .yml, .yaml, .json, .tsv, .csv 等数据文件. 它里面的文件同样是不会被复制进网站的, 它同样是为 Liquid 服务的. 对于简单的博客这个文件夹并不重要.

_drafts/ 与 _posts/

_drafts/_posts/ 就是 Jekyll 最重要的文件了. 这两个文件夹里面就储存了所有的博文. 所有存在这两个文件夹中的 markdown 与 html 文件都会被当成博文. 但是注意, _posts/ 中的文件名必须包含日期, 比如 2022-06-18-demo.md 并且包含 front-matter 才能被编译. front-matter 是在文件头标注的的一些信息, 用 3 条横线隔开. 在 front-matter 中其实可以什么都不写, 但是它可以定义一些局域变量. 比如下面的就是我这篇博文的 front-matter.

---
title: Jekyll

tag: 服务器
---

_drafts/, 即草稿文件夹里面的是正在写的博文, 一般不会被编译. 但是在编译的时候使用 jeykll build --drafts 会编译所有的草稿, 并且根据它们最后修改时间赋予时间属性.

所有的博文都会被加入到 posts 集合中, 在 Liquid 中可以被调用以生成时间线等页面. 它们默认按日期从近到远排序.

_includes/

_includes/ 文件夹里面包含了用于 Liquid 的 html 文件. 这个文件夹主要是用来做代码复用. 它包含的是一些代码碎片. 我们可以在任何页面的任何位置插入一个 _includes/ 中的代码碎片. 甚至因为 Liquid 的强大功能我们甚至能在这个文件夹里插入一些 Liquid 代码, 写个简单的语法分析器, 做一些后处理工作.

_layouts/

_layouts/ 文件夹的作用与 _includes/ 相反. _layouts/ 文件夹里的文件可以把其他的页面包裹起来, 就像 Python 里的修饰器. 这主要被用来定义所有网页共用的一些 html 元素, 比如头部, 注脚, 分栏之类的元素.

_sass/

_sass/ 文件夹其实并不是 Jekyll 本体的一部分. _sass/ 里面存放的是一些 scss 和 sass 文件. 这说起来就很复杂了. 它们是 css 文件的母文件. 可以用 Jekyll 自带的 sass 插件把它们编译成 css 文件. 而 css 文件定义了网页中每一个元素的大小, 颜色, 位置等所有的视觉效果, 动画效果等.

index.html

最后的 index.html 就是网站的主页了. 在网站中, index.html 代表的是当前文件夹本身对应的页面. 比如说我们有一个 foo/ 文件夹, 里面又有一个 foo/bar.html 文件. 这个网页 foo/bar.html 的 url 就是 https://域名/foo/bar. 但是我们在日常访问网页的时候我们同样可以访问 https://域名/foo/. 这实际上访问的就是 foo/index.html 文件. 所以我们文件夹根目录下的 index.html 文件就是访问 https://域名/ 时会看到的网页.

建站

虽然我上面介绍了这么多结构, 但其实自己想建站的话并不需要一上来就理解所有这些概念. 最简单的办法其实就是在网上找一个模板, 直接套用就好. Jekyll 的官网给出了很多模板站: https://jekyllrb.com/docs/themes/. 但是我还是比较推荐 minima 主题. 因为 minima 是最简洁的模板, 很容易在上面个人定制自己想要的内容. 但是如果是不想自己写模板的话, 推荐使用 GitHub Pages 自带的主题. 它们可以直接在 GitHub Pages 上找到: https://pages.github.com/themes/. 直接点链接, 然后在 Git 仓库的右边找到最新的包下载即可. 解压后在根目录就可以使用 bundle exec jekyll serve 看到编译的效果了. Jekyll 的 serve 功能默认会在 http://localhost:4000 host 网站, 在浏览器中输入就可以访问.