前言
我是一位程序猿。有时遇到技术难题或者需要使用新技术,不可避免得就需要到各个网页上寻求答案,我会把这些网页保存在浏览器的书签中,基本上这些答案不是一个网页就可以解决的,而是需要我们在 N 多个网页中提炼出我们需要的答案,日积月累,我的书签越来越多。再者,在开发过程中,我也会发现一些开发技巧,但是苦于没有地方记录,时间一长也就忘记了。后来就萌生了书写博客的想法,记录下自己学习的过程、点滴,也为了分享自己的收获。
最初在 CSDN 上写了几篇博客,那是一个不错的社区,里面很多牛人,也有不错的浏览,但是仍感觉不爽,那时 CSDN 还不支持 Markdown,也不支持离线保存,编辑很痛苦。要写一篇博客,需要打开网页,登陆账号,创建博客,中途没有写完,只能先保存到草稿箱,下次再打开网页登陆账号,找到这篇没有写完的博客接着编辑,过程很麻烦。所以我一般都是在本地中写好了内容,在粘到编辑器中,但是格式化又是一个很头痛的事情,经常需要个把小时来整理格式。再后来,就好久没有写博客了。
直到,后来发现了 Github Pages,我可以在 Github 网站上搭建 Blog。我既拥有绝对管理权,又享受 Github 带来的便利——不管何时何地,只要向主机提交,我就可以发布新文章。而且这些都是免费的,无限流量的。我可以像写代码一样写 Blog。
本篇文章,就是我自己在 Github 上搭建 Blog 的过程。你可以从中掌握 Github 的 Pages 功能,以及 Jekyll 软件的基本用法。
必备的技术
听起来挺高大上啊,别害怕,都是我们这些码农天天用的。所以,Github Pages 完全不在话下。
其他需要的技术下面会有介绍。
Github Pages
Github Pages 是 Github 设计的,允许用户自定义项目首页,用来替代默认的源码列表。所以,Github Pages 可以被认为是用户编写的、托管在 Github 上的静态网页。
Github 提供模版,允许站内生成网页,也允许用户自己编写网页,然后上传。
Github Pages 的优点:
- 轻量级的博客系统,没有麻烦的配置
- 免费空间,享受 Git 版本管理功能
- 使用 Markdown
- 无需自己搭建服务器
- 可以绑定自己的域名
Github Pages 的缺点:
- 使用 Jekyll 模版系统,相当于静态页发布,适合博客,文档介绍等
- 动态程序的部分相当局限,比如没有评论,不过还好我们有解决方案
- 基于 Git,很多东西需要定制,不像 Wordpress 有强大的后台
- 根据 GitHub 的限制,对应的每个站有 1GB 免费空间
- 不适合大型网站,因为没有用到数据库,每运行一次都必须遍历全部的文本文件,网站越大,生成时间越长(可以通过上传 Jekyll 本地最终生成的网页来解决)
- 网站源码基本上公开,被人 Fork 后,文章转载泛滥
大致介绍如此,下面说说 Github Pages 建立博客的过程。在使用 Github Pages 的之前,首先要注册一个 Github 的账户,点击 Github 前往。
两种 Pages 模式
与 Github 建立好链接后,我们就可以使用 Pages 服务了,Github Pages 分两种模式:
- User or organization site
- Project site
User or organization site 是在 Github 下面创建一个形如 username.github.io
的仓库, username
是你的 Github 的用户名或组织名称。每个账户下面只能建立一个,且名称必须符合这样的命名规则 username.github.io
。
Project site 是依附已有项目的 Project Pages。gh-pages 分支用于构建和发布。如果 User or organization site 使用了独立域名,那么托管在账户下的所有 Project pages 将使用相同的域名进行重定向,除非 Project pages 使用了自己的独立域名。如果没有使用独立域名,Project Pages 将通过子路径的形式提供服务 username.github.com/projectname
。 User or organization site 和 Project site 可以组合使用,通过 user or organization site 建立主站,而通过 Project Pages 挂载二级应用页面。
本篇博客主要介绍通过 User or organization site 的方式建立博客。
创建过程
1、创建个人站点
点击 创建仓库,输入形如 username.github.io
的仓库名称。
2、设置站点主题
Github Pages 提供了一些网站主题样式,如果我们不想麻烦的设置网站主题,我们可以选择 Github Pages 提供的这些主题。下面会有设置过程。
当然,如果对这些主题都不满意,我这里也推荐一个免费的主题网站,点击 jekyll 网站主题样式 进入。那么本节‘设置站点主题’就可以跳过了,不需要在看了。
进入资源设置页
找到选择主题按钮
确认发布主题
3、浏览网页
上面设置完以后,我们就可以在浏览器中输入网址 zerone-z.github.io,浏览我们的博客了。不出意外,目前应该是一篇空白,哈哈,别急,接着往下看。
Jekyll 博客网站生成器
Jekyll 是一个简单的博客形态的静态站点生产机器。它有一个模版目录,其中包含原始文本格式的文档,通过一个转换器(如 Markdown 和我们的 Liquid 渲染器转化成一个完整的可发布的静态网站,你可以发布在任何你喜爱的服务器上。
同时,Jekyll 也是 Github Pages 后台运行的默认引擎,所以你可以使用 GitHub 的服务来搭建你的项目页面、博客或者网站,而且是完全免费的。
那么,就很明显了,我们可以在本地编写符合 Jekyll 规范的网站源码,然后上传到 Github,由 Github 生成并托管整个网站。
Jekyll 本地环境搭建
由于本人使用的是 macOS 系统,那么下面主要介绍的就是在 macOS 系统下的环境搭建。
Jekyll 使用动态脚本语言 Ruby 写成。请首先下载并安装 Ruby。对于 Mac 用户,可以使用 Homebrew 安装。
打开终端,输入如下命令:
$ brew install ruby
然后安装 Jekyll:
$ gem install jekyll
后续更新 Jekyll:
$ gem update jekyll
Gem 安装总是失败或者很慢
此时我们需要更换 RubyGems 的源。建议更换成 Ruby-China 的源,参考 https://gems.ruby-china.org/。
$ gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
macOS 10.10 以后的版本安装失败
从 Mac OSX EI Capitan 开始,Apple 采取了一个叫 SIP 的东西保护系统文件夹,导致/usr/bin
等文件夹无法写入。因此需要更换安装路径。
# 安装 jekyll(OS X 10.11 以前)
$ sudo gem install jekyll
# 安装 jekyll(OS X 10.11 后苹果升级了安全策略)
$ sudo gem install -n /usr/local/bin jekyll
卸载 Jekyll
如果突然有一天不想用 Jekll,想卸载掉,那么就执行如下命令:
$ sudo gem uninstall jekyll
使用 Bundler 安装 Jekyll
为了确保本地 Ruby 的开发环境时刻与 Github Pages 运行环境一致,建议采用本节介绍的安装方式,方便管理 RubyGems 依赖组件。
Dependency versions 列出了当前 Github Pages 使用的各个 Gem 组件版本。下面是安装过程:
NOTE: Jekyll 3.3.0 之后开始采用 Bundler 方式运行。我们只需要安装 Bundler,然后通过 Bundler 来安装运行 Jekyll。
首先到 Download Ruby 下载安装 Ruby,执行以下命令行检查确保版本是1.9.3
或2.0.0
。
$ ruby --version
安装 Bundler,负责管理 RubyGems 组件的版本。
$ gem install bundler
如果安装失败,执行如下命令重新安装。
# OS X 10.11 以前的安装命令
$ sudo gem install bundler
# OS X 10.11 后苹果升级了安全策略,安装命令如下
$ sudo gem install -n /usr/local/bin bundler
Bundler 安装成功后,最后要安装 Jekyll 及依赖的 gem 组件。
# 初始安装环境
$ bundler install
# 更新环境
$ bundler update
bundler install
命令会主动把 Jekyll 以及其他的一些依赖包安装好,无需我们手动安装 Jekyll。当一切都安装好以后,我们只需要在原来 Jekyll 命令的基础上加上前缀bundle exec
即可运行,事例如下:
$ bundle exec jekyll serve
Jekyll 基本用法
安装完 Jekyll 以后,我们就可以使用 Jekyll 的命令了。如下所示:
$ jekyll build
# => 当前文件夹中的内容将会生成到 ./_site 文件夹中。
$ jekyll build --destination <destination>
# => 当前文件夹中的内容将会生成到目标文件夹<destination>中。
$ jekyll build --source <source> --destination <destination>
# => 指定源文件夹<source>中的内容将会生成到目标文件夹<destination>中。
$ jekyll build --watch
# => 当前文件夹中的内容将会生成到 ./_site 文件夹中,
# 查看改变,并且自动再生成。
Note: 在自动构建过程中对
_config.yml
的修改将不会被包含其中。但是数据文件 (DataFiles) 在自动构建过程中会被包含和加载。
<destination>
文件夹会在站点建立时被清理。
Jekyll 同时也集成了一个开发用的服务器,可以让我们使用浏览器在本地进行预览。
$ jekyll serve
# => 一个开发服务器将会运行在 http://localhost:4000/
# Auto-regeneration(自动再生成文件): 开启。使用 `--no-watch` 来关闭。
$ jekyll serve --detach
# => 功能和`jekyll serve`命令相同,但是会脱离终端在后台运行。
# 如果你想关闭服务器,可以使用`kill -9 1234`命令,"1234" 是进程号(PID)。
# 如果你找不到进程号,那么就用`ps aux | grep jekyll`命令来查看,然后关闭服务器。
Jekyll 目录结构
Jekyll 的核心其实是一个文本转换引擎。其内部的核心工作原理就是:你用你最喜欢的标记语言来写文章,可以是 Markdown, 也可以是 Textile, 或者就是简单的 HTML, 然后 Jekyll 就会帮你套入一个或一系列的布局中。在整个过程中你可以设置 URL 路径,你的文本在布局中的显示样式等等。这些都可以通过纯文本编辑来实现,最终生成的静态页面就是你的成品了。
Jekyll 网站的基本的目录结构一般是像这样的:
.
├── _config.yml
├── _drafts
| ├── begin-with-the-crazy-ideas.textile
| └── on-simplicity-in-technology.markdown
├── _includes
| ├── footer.html
| └── header.html
├── _layouts
| ├── default.html
| └── post.html
├── _posts
| ├── 2007-10-29-why-every-programmer-should-play-nethack.textile
| └── 2009-04-26-barcamp-boston-4-roundup.textile
├── _site
├── .jekyll-metadata
└── index.html # 也可以是 index.md
下面就来简单介绍下它们的作用:
_config.yml
Jekyll 站点的总配置文件,虽然有很多选项也可以通过命令行方式指定(如:source、destination),但是如果写在配置文件中,我们就不需要记住这些命令了,而且设置之后就不用关心了。
_drafts
未发布的草稿,文件名不需要带有日期。
_includes
存放一些小的可复用的代码片段。可以通过标签{% include file.ext %}
来把文件_includes/file.ext
包含进来。
_layouts
这里存放着布局模板文件。在我们的博客中可以在 YAML 头信息中根据不同文章选择不同的布局。这将在下一个部分进行介绍。我们可以通过Liquid
标签{{ content }}
可以将博客内容插入布局模板中。
_posts
这里存放的就是我们的博客了。博客文件的命名格式必须符合:YEAR-MONTH-DAY-title.MARKUP
,如:2017-08-02-我的博客。md
。MARKUP 是我们所使用的标记语言的文件后缀名,这里我使用的是 Markdown,所以是用了md
后缀,如果是 html,那么可以命名为2017-08-02-我的博客。html
。
_site
这里是 Jekyll 用以存放最终生成站点的根路径位置。可以加入.gitignore
列表中。
index.html/index.md/index.textile
网站首页。
其他
任何以下划线开头的文件和目录都不会成为网站的一部分。
Jekyll 配置
Jekyll 的配置写在_config.yml
文件中,配置项有很多,这里就不一一去说明了,很多配置虽有用但是一般我们不需要去关心,点击 官方配置文档 可以去详细的了解。下面主要说明一些重要的配置。
先看一下’_config.yml’中的默认配置:
# 目录结构
source: .
destination: ./_site
plugins: ./_plugins
layouts: ./_layouts
data_source: ./_data
collections: null
# 阅读处理
safe: false
include: [".htaccess"]
exclude: []
keep_files: [".git", ".svn"]
encoding: "utf-8"
markdown_ext: "markdown,mkdown,mkdn,mkd,md"
# 内容过滤
show_drafts: null
limit_posts: 0
future: true
unpublished: false
# 插件
whitelist: []
gems: []
# 转换
markdown: kramdown
highlighter: rouge
lsi: false
excerpt_separator: "\n\n"
incremental: false
# 服务器选项
detach: false
port: 4000
host: 127.0.0.1
baseurl: "" # does not include hostname
# 输出
permalink: date
paginate_path: /page:num
timezone: null
quiet: false
defaults: []
# Markdown 处理器
rdiscount:
extensions: []
redcarpet:
extensions: []
kramdown:
auto_ids: true
footnote_nr: 1
entity_output: as_char
toc_levels: 1..6
smart_quotes: lsquo,rsquo,ldquo,rdquo
enable_coderay: false
coderay:
coderay_wrap: div
coderay_line_numbers: inline
coderay_line_number_start: 1
coderay_tab_width: 4
coderay_bold_every: 10
coderay_css: style
输出格式配置(Permalink)
Permalink
用来定义我们博客的永久链接地址。默认配置为date
。
永久的链接模版用以冒号为前缀的关键词标记动态内容,比如date
代表/:categories/:year/:month/:day/:title.html
。有如下变量:
year
:文件名中的年份,如:2017month
:文件名中的月份,如:08day
:文件名中的日期,如:09title
:文件名中的文章标题,可以被文件头信息中的slug
覆盖categories
:文章的分类,如果没有,则忽略short_year
:文件名中去除前缀世纪数的年份,如:17i_month
:文件名中去除前缀 0 的月份,如:8i_day
:文件名中去除前缀 0 的日期,如:9hour
:时钟,24 小时制,以文章的头信息中date
为基准。(00..23)minute
:当前时钟内的分钟,以文章的头信息中date
为基准。(00..59)second
:当前时钟内的秒钟,以文章的头信息中date
为基准。(00..59)slug
:由文件的文件名中确定的 Slugified title(除数字和字母之外的字符们会被取代为连字符),可以被文件头信息中的 slug 覆盖
假设一篇文章名为2017-08-22-myzerone.md
,那么最终的配置效果如下:
permalink: pretty
=> /2017/08/22/myzerone/index.htmlpermalink: /:month-:day-:year/:title.html
=> /2017-08-22/myzerone.htmlpermalink: /blog/:year/:month/:day/:title
=> /blog/2017/08/22/myzeronepermalink: /:title
=> /myzerone
自定义配置
_config.yml
不仅可以设置系统的配置项,我们也可以加入一些我们自定义的配置项,例如我们定义了title: myzerone 的博客
这样的一项,那么我们就可以在文章中使用{{ site.title }}
来引用这个变量。我的自定义配置如下:
# 站点设置
title: myzerone
bio: '嗨,大家好,我是一位来自中国的 iOS 开发者'
description: "记录下自己学习的过程、点滴,也为了分享自己的收获。"
Markdown 配置
Github 宣布从 2016 年 05 月 01 号起,Github 将只支持 kramdown 作为唯一的 Markdown 引擎。那么我们只要在_config.yml
写入如下配置即可:
markdown: kramdown
kramdown:
input: GFM
highlighter: rouge
本地如果解析 Markdown 失败的话,那么就需要我们安装 kramdown 包
$ gem install kramdown
头信息
Jekyll 整个站点的配置是在站点根目录下的_config.yml
文件中,而_layouts
,_posts
等目录下的文件中也可以有自己的配置变量,这些配置变量称为头信息 (Front Matter)。
任何只要包含 YAML 头信息的文件在 Jekyll 中都能被当做一个特殊的文件来处理。头信息必须在文件的开始部分,并且需要按照 YAML 的格式写在两行三虚线之间。下面是一个基本的例子:
---
layout: post
title: myzerone 的博客
---
在这两行的三虚线之间,你可以设置预定义的变量(下面这个例子可以作为参考),甚至创建一个你自己定义的变量。这样在接下来的文件和任意模板中或者在包含这些页面或博客的模板中都可以通过使用 Liquid 标签来访问这些变量。
Note:头信息变量是可选的,如果你想使用 Liquid 标签和变量但在头信息中又不需要任何定义,那你就让头信息空着!在头信息为空的情况下,Jekyll 仍然能够处理文件。(这对于一些像 CSS 和 RSS 的文件非常有用)
Jekyll 对每一个页面或者博客的都有预先设置全局变量。
layout
:设置使用_layouts
文件夹中的模版文件名称。不需要文件扩展名。permalink
:如果不想使用默认的全局设置,那么这里就单独设置本博客的永久链接地址。published
:如果不想发布本博客,那么就设置该变量为 false。date
:这里的日期会覆盖博客名称中的日期。格式:YYYY-MM-DD HH:MM:SS +/-TTTT
,时分秒可选。category/categories
:指定博客的分类,如:[Github Pages, Blog, Jekyll]
tags
:类似分类categories
。
当然这里也可以设置自定义变量。
例如在头信息中设置了 title 变量:
---
layout: post
title: myzerone 的博客
---
那么我们就可以在模板文件中使用 title 这个变量了:
<!DOCTYPE HTML>
<html>
<head>
<title>{{ page.title }}</title>
</head>
<body>
...
Jekyll 解析引擎
通过以上的说明,我们还需要明确一点,Jekyll 是解析引擎,不是模板系统,它是用来解析特定符号标记的文本内容,根据模板样式转出相应布局的 HTML 文件,这个模板就需要我们自己来设计了,如_layouts
文件夹下的文件就是我们设计的模板文件。
使用 Jekyll 中的模板,我们需要理解两个概念: Templates 和 Content 。Templates 是我们设计的模板(_layouts
),而 Content 则是要放进前面设计的模板中的东西(_posts
),两者的结合就是输出的整个静态网站。
Jekyll 中的 Content
对与 Jekyll 而言,就是 page 或 post 对象,它们会被写入 Templates 中生成最终的静态文件,也就是 html。这里的 Content,我们可以用 Markdown、html 或者 textile 来写,其中可以包含 Liquid 模板语法命令来修饰特点的内容。在前面,我说过 头信息 的一个全局变量layout
,这个就是用来指定该 Content,应该使用哪一个 Templates,这样 Jekyll 运行时,才能正确的把 Contentx 写入 Templates 中,生成最终的 html 静态文件。
Jekyll 中的 Templates
通过以上内容,我们已经知道 Templates 是用来包含 post 或 page 中的内容的,那么应该如何设计这个模板(Templates)呢?
Jekyll 使用 Liquid 模板语言,支持所有的 Liquid 标签和过滤器,甚至增加了几个过滤器和标签。那么这里,当然是采用 Liquid 代码和 HTML/CSS 来设计啦。Templates 文件都被放置在_layouts
目录中,模板文件可以访问 Jekyll 的全局变量site
和当前页page
对象。Jekyll 解析时,会把 post 或者 page 中的内容放在 Templates 使用content
变量的地方,如:
<head>
<title>{{ page.title }}</title>
</head>
<body>
<div id="sidebar"> ... </div>
<div id="main">
{{ content }}
</div>
</body>
建站实例
Jekyll 真正的做到了一劳永逸,搭建网站时做好配置文件、模板文件,以后,我们只需要关注 Blog 本身就可以了,而不用关注 Blog 以外的东西。简单地说,就是在写博客的过程中,我们只需要管理我们电脑中的一个文件夹的文本文件就可以写好 Blog,并随时发布到线上。下面就来说说具体的搭建过程,让我们能够对 Jekyll 的整个搭建过程有一个清晰的认识。
1、创建项目
如果我们已经建立了 Github Pages 仓库,那么我们就 Clone 我们的仓库到本地就可以了。如果没有,或者只是想在本地试试 Jekyll 的功能,那么我们就在我们的电脑中创建一个文件夹。假设文件夹名为:myzerone_demo
。
2、创建配置文件
在myzerone_demo
文件夹下,创建一个名为_config.yml
的文本文件。这就是 Jekyll 的配置文件。我在里面填入如下配置内容,其他的可以用默认选项,具体参考 Jekyll 的配置。
# Site settings
title: myzerone Blog
baseurl: ''
# Build settings
markdown: kramdown
目录结构变为:
/myzerone_demo
| -- _config.yml
3、定义模板
在项目根目录下,创建一个_layouts
目录,用于存放模板文件。进入该目录创建一个default.html
文件,用于 Blog 的默认模板,并写入一下内容:
<!DOCTYPE html>
<html>
<head>
<title>{{ page.title }}</title>
</head>
<body>
{{ content }}
</body>
</html>
这里的{{ page.title }}
和{{ content }}
使用了 Liquid 模板语言,下面会有介绍。
现在目录结构变成:
/myzerone_demo
|-- _config.yml
|-- _layouts
| |-- default.html
4、创建文章
回到根目录下创建一个_posts
目录,用于存放 Blog 文章。进入该目录,创建一个文本文件,文件名定义为2017-08-22-myzerone blog.md
。在该文件中填入一下内容:
---
layout: default
title: myzerone 的博客
---
This is my first post.
在上面提到过,每篇文章的头部,必须有一个 yaml 的头信息,用来设置一些元数据,这些元数据被包在以三根短划线---
为开始和结束的位置中,每一行表示一种元数据。layout: default
表示改文章使用_layouts
目录下的default.html
文件作为模板。title: myzerone 的博客
表示改文章的标题为“myzerone 的博客”,如果不设置这个值,则默认为文件名中的标题,即:“myzerone blog”。 在 yaml 的头信息后面,就是文章的内容了,里面也可以使用模版变量。
现在目录结构变成:
/myzerone_demo
|-- _config.yml
|-- _layouts
| |-- default.html
|-- _posts
| |-- 2014-12-25-hello-world.md
5、创建首页
回到根目录,创建一个index.html
文件,写入一下内容,链接上面的文章:
---
layout: default
title: My Blog
---
<h2>{{ page.title }}</h2>
<p>Recent Blog Posts</p>
<ul>
{% for post in site.posts %}
<li>{{ post.date | date_to_string }}
<a href="{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
这里 YAML 头信息定义了default
作为模板文件,标题为“My Blog”。然后{% for post in site.posts %}
是 Liquid 命令语句,表示遍历所有的文章。
现在目录结构变成:
/myzerone_demo
|-- _config.yml
|-- _layouts
| |-- default.html
|-- _posts
| |-- 2014-12-25-hello-world.md
|-- index.html
6、发布/浏览内容
如果是 Clone 的 Github 仓库,现在就可以提交到 Github 上了,稍等一会,访问https://username.github.io
,就可以看到你的博客内容了。
也可以使用本地的 Jekyll 服务,浏览你的内容。
打开终端,输入如下命令,进入你的myzerone_demo
目录:
$ cd /myzerone_demo
# => 这里输入你的文件夹路径
启动 Jekyll 服务:
$ jekyll serve
等服务启动成功,在浏览其中输入地址http://127.0.0.1:4000/
,就可以看到你的博客内容了。
到此一个简单的 Blog 就算搭建完成了,以后写的博客只需要在_posts
目录下新建一个文本文件,然后在这个文本文件中写入你的内容就好了。
Jekyll 快速创建
看着上面有好多的步骤,是不是感觉有一点麻烦,那么下面就说一个简单的方式。
其实,Jekyll 自身是可以生成前面所说的完整的目录结构的,完全不需要我们一个一个的创建目录。
首先打开终端,进入我们放置博客的目录下:
$ cd ~/Desktop
# => 我这里是放置在桌面上
然后运行 Jekyll 的命令:
$ jekyll new myzeroneBlog
# => `myzeronBlog`就是放置博客的文件夹的名称
那么现在的文件结构如下:
/Desktop
|-- myzeroneBlog
|-- _config.yml
|-- _layouts
| |-- default.html
|-- _posts
| |-- 2017-08-23-welcome-to-jekyll.markdown
|-- -includes
|-- _sass
|-- css
| |-- main.scss
|-- about.md
|-- feed.xml
|-- index.html
这样就可以快速的创建一个网站实例,可以节省不少时间。
Github Pages 域名相关设置
Github Pages 虽然为每一个人提供了一个二级域名username.github.io
,但是对于喜欢个性有爱折腾的我来说,使用别人的域名是万万不能忍受的,下面就来介绍一下 Github Pages 与域名之间不得不说的故事。
自定义域名
英语不错的小伙伴可以移步官方文档 Using a custom domain with GitHub Pages,跳过本节。如我这样稀松平常的,就看下面的流程吧。
首先,我们需要有个自己的域名,比如myzerone.com
,这里就不介绍域名的注册过程了,可自行搜索。然后,我的目标是绑定到myzerone.github.io
,并支持所有二级域名的跳转,如从 myzerone.github.io/posts/ 自动跳转到myzerone.com/posts/
。
1、创建 CNAME 文件
在使用我们的 DNS 提供商设置自定义域名之前,你需要添加一个自定义域名到你的 Github Pages 仓库中,也就是在你的’username.github.io’仓库的根路径下创建一个CNAME
文件。这里有两种方式:
方式一:
进入你的 Github Pages 的仓库,点击Settings
在Settings
下,找到Custom domain
项目,输入你的域名,然后保存。
方式二:
直接在你的username.github.io
仓库下新建文件CNAME
,在该文件中写入你的域名。这样一个简单的 CNAME 文件就创建好了。
通过方式一,你会发现,其实最终也是在’username.github.io’仓库下创建了一个文件CNAME
,里面也写入了你的域名,只不过第一种方式是由 Github 自动帮你创建的,二方式二,是我们自己创建的。
NOTE:
- 在 CNAME 中填入的域名不能包含前缀信息,即不能添加
http://
前缀- CNAME 文件的文件名必须是大写,否则 Github Pages 无法识别解析
- 每个 CNAME 文件能且只能指定一个域名
2、CNAME 绑定域名
因为我的域名是在阿里云的万网 DNS 服务器上,那么下面主要说的就是阿里云的操作步骤。当然如果你是用其他的 DNS 服务器,除了 DNS 服务器不一样以为,其他的设置(如 A 和 CNAME 记录)均相同。
再说具体的步骤之前,先简要说一下 A/CNAME 记录的意义,方便理解。A 记录就是直接指定一个 IP,CNAME 就是重命名,指向另一个域名。
步骤:
- 进入阿里云域名控制台,进入该域名的解析设置
- 设置主机记录
www
,记录类型为 A,记录值是 IP192.30.252.153
。其中192.30.252.153
是 Github Pages 服务器指定的 IP 地址,访问该 IP 地址即表示访问 Github Pages。 - 设置主机记录
www
,记录类型为 A,记录值是IP192.30.252.154
。同上。 - 设置主机记录
@
,记录类型为 CNAME,记录值是myzerone.github.io
。表示将http://myzerone.com
这个主域名映射myzerone.github.io.
。在这里千万不要忘记记录值中.io
后面还有一个点.
!
以上设置完以后的效果如下:
如果我们设置的是顶级域名,那么到此就结束了,可以看下一节内容了。 但是,有时,我们只想将子域名邦定到博客地址。比如想将blog.myzerone.com
(即主域名myzerone.com
的子域名)映射到myzerone.github.io
中,此时怎么办呢?下面就说一下子域名绑定到博客地址的步骤:
- 首先你的 Github Pages 仓库项目中的 CNAME 文件内容应该修改为
blog.myzerone.com
。 - 设置主机记录
blog
,记录类型为 CNAME,记录值是myzerone.github.io.
。
漫长的等待
在以上都设置完成以后,就是一个漫长的等待了,因为要等全球解析生效以后,我们的这些设置才会生效。在等待过程中,我们可以ping
一下,看看我们的设置对不对。等生效以后,我们就可以通过我们的域名访问博客了。
自定义域配置 HTTPS
NOTE: 如果你没有自定义域名,那么你可以跳过本节的内容。
HTTPS 是 HTTP 的安全版,是一种更安全的 HTTP 协议,目前大部分公司,像 Google、Baidu、Facebook、Taobao 等公司,都已经启用 HTTPS 了,启用 HTTPS 的网站,可以在浏览器的地址栏看到一个绿色的锁的标志,说明这是一个安全的网站,可以放心使用,所以建议我们的博客网站最好也启用 HTTPS。
虽然 Github Pages 提供的子域名username.github.io
是支持 HTTPS 的,但是当你开启了自定义域名以后,Github 提供的 HTTPS 就无法生效了,本节就是介绍,如何在自定义域名的基础上使 HTTPS 生效。
选用方案
虽然免费的 SSL 服务有很多,比如 Let’s Encrypt,提供免费、自动化、开放的证书签发服务,得到了诸如 Mozilla 等众多知名互联网公司和机构的支持。
但是 Github Pages 不支持证书的上传,且无法配置证书文件,所以无论你是 Jekyll,还是 Hexo,即使申请购买到了 HTTPS 的证书也无法使用。所以如果想要给你的网站配置 HTTPS,就不能使用一般的静态证书,只能在 DNS 域名解析的时候给你提供一个动态的 HTTPS 证书。
那么我们可以采用 Cloudflare 提供的 DNS 域名解析服务,并绑定动态的 HTTPS,既不需要申请证书,也不用保存证书,所有的服务都是在 DNS 那里动态的加载 HTTPS,而且最重要的是,这一切都是免费的,你现在就可以看到我的博客前面都有一个绿色的锁,用的就是 Cloudflare。
更改域名解析服务到 Cloudflare
如果你还没有 Cloudflare 账号,点击 这里注册,已有的话,点击 这里登录。登录以后,点击 这里添加 你的域名,输入你的域名,如myzerone.com
并点击Begin Scan
开始扫描。
大约等待 60 秒即可完成域名解析扫描。完成后点击Continue Setup
继续下一步。
看到 DNS 记录(包括子域)列表以后,按照下图提示设置后,点击Continue
下一步(CNAME 是为了重定向 www 准备的)。
选择免费服务计划(Free Website),然后点击Continue
下一步。
这时你会看到 Cloudflare 会让你修改 DNS 解析服务,列出了当前解析服务地址,以及你需要修改后的解析服务地址。
此时,我们需要到我们的域名提供商的控制面板中修改 DNS 服务。因为我域名是在阿里的万网,所以这里以万网为例,首先找到我们的域名,在该域名管理
中选择DNS 修改/创建
。
在我们的域名提供商那里修改好 DNS 服务以后,再回到 Cloudflare,点击Continue
继续下一步。
此时会进入一个等待 DNS 服务生效的界面,官方说最长需要 24 小时生效,但是根据我的经验速度还是挺快的,大约需要等待 5~30 分钟即可。一般等个 5 分钟后可以刷新界面,或者点击Recheck Nameservers
。
直到界面显示Status: Active
即可。
设置 SSL,绑定动态的 HTTPS
上一节介绍了如何把我们的域名服务转移到 Cloudflare 上,本节就介绍我们的域名如何绑定 HTTPS。
首先在 Cloudflare 上进入我们的域名myzerone.com
,然后点击Crypto
菜单,设置Flexible SSL
(也可以保持 Full)。
NOTE:官网说明这里可能需要 24 小时后才能生效,然后以我的经验,确实需要好久大概有 12 个小时。所以这里设置以后,在浏览器中输入我们的域名发现仍然提示是不安全的 HTTPS,不要急,睡一觉,明天再看看效果。
上面的设置以后说明我们的博客已经支持 HTTPS 了,但是,如果用户输入的是http://myzerone.com
,那么用户仍然没有使用我们的 HTTPS 服务,我们要如何让用户始终默认访问的都是 HTTPS 服务呢?接下来就介绍一下 Cloudflare 提供的另一个功能了重定向
。
点击域名下的另一个菜单Page Rules
,然后点击Create Page Rule
。
添加自动使用 HTTPS,强制使用 SSL,可以使用通配符*
。
完成
上面的步骤完成后,我们的博客就已经支持 HTTPS 了。但是因为 SSL 的设置需要生效时间,我的就大概等了有 12 个小时才生效,所以不要急,今天不行,明天再看看,说不定就生效了。
绑定多个域名
首先,申明一点:Github Pages 是不支持设置多个域名的。也就是说,一个username.github.io
只能支持一个域名。
虽然官方不提供设置多个域名的方法,但是还有其他的一些方式可以用。比如我这里通过Cloudflare
提供的 CDN 做转发实现了‘绑定多个域名’。具体方法如下:
首先按照上一节的【更改域名解析服务到 Cloudflare】的步骤把我们的另一个域名添加到 Cloudflare 中。 然后选择该域名下的菜单Page Rules
,然后点击Create Page Rule
,设置如下,实现跳转。
NOTE:这里的
*
号是通配符,$2
表示匹配第二个*
号的内容。Cloudfare 只支持简单的通配符。
如果还有其他的域名需要跳转,用同样的方法就可以实现。这样一个简单的 Github Pages 多域名绑定就是实现了。
YAML 格式化数据
Jekyll 的配置文件_config.yml
已经每个文件的头信息都是采用的 YAML 格式化数据。下面就来简要的介绍一下。点击 YAML 官网 详细了解。
基本介绍
- 大小写敏感
- 使用缩进表示层级关系
- 缩进时不允许使用 Tab 键,只允许使用空格
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
#
表示注释,从这个字符一直到行尾,都会被解析器忽略
YAML 有三种数据结构:
- 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
- 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
- 纯量(scalars):单个的、不可再分的值
对象
对象是一组键值对,使用冒号结构表示。
animal: pets
还有另一种写法,将所有键值对写成一个行内对象。
hash: { name: Steve, foo: bar }
数组
一组连词线开头的行,构成一个数组。
- Cat
- Dog
- Goldfish
数据结构的子成员是一个数组,则可以在该项下面缩进一个空格。
-
- Cat
- Dog
- Goldfish
数组也可以采用行内表示法。
animal: [Cat, Dog]
纯量
纯量是最基本的、不可再分的值。如下:
- 字符串
- 布尔值
- 整数
- 浮点数
- Null
- 时间
- 日期
数值直接以字面量的形式表示。
number: 12.30
布尔值用 true 和 false 表示。
isSet: true
null
用~
表示。
parent: ~
时间采用 ISO8601 格式。
iso8601: 2001-12-14t21:59:43.10-05:00
日期采用复合 iso8601 格式的年、月、日表示。
date: 1976-07-31
允许使用两个感叹号,强制转换数据类型。
e: !!str 123
f: !!str true
字符串是最常见,也是最复杂的一种数据类型。
字符串默认不使用引号表示。
str: 这是一行字符串
如果字符串之中包含空格或特殊字符,需要放在引号之中。
str: '内容: 字符串'
单引号和双引号都可以使用,双引号不会对特殊字符转义。
s1: '内容、n 字符串'
s2: "内容、n 字符串"
这里的双引号不转义\n
表示的就是换行。二单引号会转义,\n
转义后为\\n
。
单引号之中如果还有单引号,必须连续使用两个单引号转义。
str: 'labor''s day'
字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格。
str: 这是一段
多行
字符串
# 表示的是"这是一段 多行 字符串"这样的字符串。
多行字符串可以使用|
保留换行符,也可以使用>
折叠换行。
this: |
Foo
Bar
# 表示“Foo\nBar\n”
that: >
Foo
Bar
# 表示“Foo Bar\n”
+
表示保留文字块末尾的换行,-
表示删除字符串末尾的换行。
s1: |
Foo
# “Foo\n”
s2: |+
Foo
# “Foo\n\n\n”
s3: |-
Foo
# “Foo”
字符串之中可以插入 HTML 标记。
message: |
<p style="color: red">
段落
</p>
# “\n<p style="color: red">\n 段落、n</p>\n”
锚点&
和别名*
,可以用来引用。
defaults: &defaults
adapter: postgres
host: localhost
development:
database: myapp_development
<<: *defaults
test:
database: myapp_test
<<: *defaults
等同于下面。
defaults:
adapter: postgres
host: localhost
development:
database: myapp_development
adapter: postgres
host: localhost
test:
database: myapp_test
adapter: postgres
host: localhost
&
用来建立锚点(defaults
),<<
表示合并到当前数据,*
用来引用锚点。
- &showell Steve
- Clark
- Brian
- Oren
- *showell
等同于下面。
- Steve
- Clark
- Brian
- Oren
- Steve
Liquid 模板语言
Jekyll 使用 Liquid 模板语言,支持所有标准的 Liquid标签 和 过滤器。Jekyll 甚至增加了几个过滤器和标签,方便使用。下面就来简单介绍一下 Liquid 的使用。
Liquid 语法索引
Liquid 有两种标记:
- 输入块:
{{ 输出内容 }}
- 逻辑块:
{% 逻辑语句 %}
过滤器
就是输入标记中国年带有过滤器的功能。第一个参数总是过滤器左边值的输出。当下个过滤器运行时,刚刚所得到的过滤器返回值就会成为新的左边值。直到最后没有过滤器时,模板就会接受最后的结果字符串。
Hello {{ 'tobi' | upcase }}
Hello tobi has {{ 'tobi' | size }} letters!
Hello {{ 'tobi' | capitalize }}
Hello {{ '1984-02-01' | date: "%Y" }}
输出结果为:
Hello TOBI
Hello tobi has 4 letters!
Hello Tobi
Hello 1984
标准过滤器如下:
date - 格式化日期
capitalize - 将输入语句的首字母大写
downcase - 将输入字符串转为小写
upcase - 将输入字符串转为大写
first - 得到传递数组的第一个元素
last - 得到传递数组的最后一个元素
join - 将数组中的元素连成一串,中间通过某些字符分隔
sort - 对数组元素进行排序
map - 从一个给定属性中映射/收集一个数组
size - 返回一个数组或字符串的大小
escape - 对一串字符串进行编码
escape_once - 返回一个转义的 html 版本,而不影响现有的转义文本
strip_html - 去除一串字符串中的所有 html 标签
strip_newlines - 从字符串中去除所有换行符 (\n)
newline_to_br - 将所有的换行符 (\n) 换成 html 的换行标记
replace - 匹配每一处指定字符串,如 {{ 'foofoo' | replace:'foo','bar' }} #=> 'barbar'
replace_first - 匹配第一处指定的字符串,如 {{ 'barbar' | replace_first:'bar','foo' }} #=> 'foobar'
remove - 删除每一处匹配字符串,如 {{ 'foobarfoobar' | remove:'foo' }} #=> 'barbar'
remove_first - 删除第一处匹配的字符串,如 {{ 'barbar' | remove_first:'bar' }} #=> 'bar'
truncate - 将一串字符串截断为 x 个字符
truncatewords - 将一串字符串截断为 x 个单词
prepend - 在一串字符串前面加上指定字符串,如 {{ 'bar' | prepend:'foo' }} #=> 'foobar'
append - 在一串字符串后面加上指定字符串,如 {{ 'foo' | append:'bar' }} #=> 'foobar'
minus - 减,如 {{ 4 | minus:2 }} #=> 2
plus - 加,如 {{ '1' | plus:'1' }} #=> '11', {{ 1 | plus:1 }} #=> 2
times - 乘,如 {{ 5 | times:4 }} #=> 20
divided_by - 除,如 {{ 10 | divided_by:2 }} #=> 5
split - 将一串字符串根据匹配模式分割成数组,如 {{ "a~b" | split:~ }} #=> \['a','b'\]
modulo - 余数,如 {{ 3 | modulo:2 }} #=> 1
逻辑块
用于我们的模板逻辑开发。目前已经支持的标签如下:
assign - 赋值
capture - 捕捉文本并赋值
case - case when 语块
comment - 注释
cycle - 几个值中间循环
for - 循环
if - 决断逻辑块
include - 包括另一个模板
raw - 原样输出
unless - 判断块的另一种写法
注释
注释是最简单的标签,它只是把内容包含起来。
{% comment %} 注释内容 {% endcomment %}
原始输出
有时需要展示一些与Liquid
产生冲突的内容时(如,我们需要展示 liquid 语句),我们需要暂时性的禁用标签的解析,这时就需要Raw
标签了。
{% raw %}
In Handlebars, {{ this }} will be HTML-escaped, but {{{ that }}} will not.
{ % endraw %}
// 在使用过程中注意去掉`{`和`%`之间的空格,因为我不这样写,我也没有办法输出它。.. 很无奈。
条件判断语句
我们可以使用 Liquid 提供的if
、unless
、elsif
或else
来实现简单的条件判断:
{% if user %}
Hello {{ user.name }}
{% endif %}
{% if user.name == 'tobi' %}
Hello tobi
{% elsif user.name == 'bob' %}
Hello bob
{% endif %}
{% if user.name == 'tobi' or user.name == 'bob' %}
Hello tobi or bob
{% endif %}
{% if user.name == 'bob' and user.age > 45 %}
Hello old bob
{% endif %}
{% if user.name != 'tobi' %}
Hello non-tobi
{% endif %}
# 同上
{% unless user.name == 'tobi' %}
Hello non-tobi
{% endunless %}
# 检测是否用户有一张信用卡
{% if user.creditcard != null %}
poor sob
{% endif %}
# 同上
{% if user.creditcard %}
poor sob
{% endif %}
# Check for an empty array
{% if user.payments == empty %}
you never paid !
{% endif %}
{% if user.age > 18 %}
Login here
{% else %}
Sorry, you are too young
{% endif %}
# array = 1,2,3
{% if array contains 2 %}
array includes 2
{% endif %}
# string = 'hello world'
{% if string contains 'hello' %}
string includes 'hello'
{% endif %}
Case 语句
如果你需要更多的条件判断,你可以使用case
语句:
{% case condition %}
{% when 1 %}
hit 1
{% when 2 or 3 %}
hit 2 or 3
{% else %}
... else ...
{% endcase %}
Cycle
我们常常需要在不同的颜色或类似的任务间轮流切换。Liquid 对于这样的操作有内置支持,通过使用cycle
标签。
{% cycle 'one', 'two', 'three' %}
will result in
one
two
three
one
如果一组cycle
没有命名,那默认情况下有用相同参数的会被认为是一个组。如果你希望完全控制cycle
组,你可以指定一个组名,这个组名甚至可以是一个变量。
{% cycle 'group 1': 'one', 'two', 'three' %}
{% cycle 'group 2': 'one', 'two', 'three' %}
will result in
one
two
one
two
循环
数组循环:
{% for item in array %}
{{ item }}
{% endfor %}
Map 循环:
{% for item in hash %}
{{ item[0] }}: {{ item[1] }}
{% endfor %}
在循环中有如下变量:
forloop.length # => 循环长度
forloop.index # => 当前索引
forloop.index0 # => 基于 0 的当前索引
forloop.rindex # => 剩余元素数
forloop.rindex0 # => 基于 0 的剩余元素数
forloop.first # => 判断当前是不是第一个元素
forloop.last # => 判断当前是不是最后一个元素
通过limit
限制循环的开始,offset
限制循环的结束:
# array = \[1,2,3,4,5,6\]
{% for item in array limit:2 offset:2 %}
{{ item }}
{% endfor %}
# results in 3,4
使用reversed
反转循环
{% for item in collection reversed %}
{{item}}
{% endfor %}
循环数字范围:
# 如果 item.quantity 的值是 4...
{% for i in (1..item.quantity) %}
{{ i }}
{% endfor %}
# results in 1,2,3,4
可以使用assign
标签给变量赋值,以便在输出或者其他标签中使用。
{% assign name = 'freestyle' %}
{% for t in collections.tags %}
{% if t == name %}
<p>Freestyle!</p>
{% endif %}
{% endfor %}
我们可以使用capture
标签把一系列字符串连接为一个字符串,并将其存储到变量中。这个标签是一个块级标签,它会captures
任何在其中渲染的元素,并把捕获的值赋给给定的变量,而不是把这些值渲染在页面中。
{% capture attribute_name %}{{ item.title | handleize }}-{{ i }}-color{% endcapture %}
<label for="{{ attribute_name }}">Color:</label>
<select name="attributes[{{ attribute_name }}]" id="{{ attribute_name }}">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
Jekyll 中的 Liquid
常用变量
对于有 YAML 头信息的文件,Jekyll 都会通过 Liquid 模板工具 来生成一系列的数据。下面就是这些可用数据变量的参考。
常用全局变量:
site
: 来自_config.yml
文件,全站范围的信息+配置page
: 页面专属的信息 + YAML 头文件信息。通过 YAML 头文件自定义的信息都可以在这里被获取content
: 被layout
包裹的那些 Post 或者 Page 渲染生成的内容。但是又没定义在 Post 或者 Page 文件中的变量。
常用全站 (site) 变量:
site.time
: 当前时间(运行 jekyll 这个命令的时间点)site.pages
: 所有 Pages 的清单。site.posts
: 一个按照时间倒序的所有 Posts 的清单。site.collections
: 一个所有集合(collection)的清单。site.data
: 一个存储了_data
目录下的 YAML 文件数据的清单。site.documents
: 每一个集合(collection)中的全部文件的清单。
常用页面 (page) 变量:
page.content
: 页面内容的源码。page.title
: 页面的标题。page.excerpt
: 页面摘要的源码。page.url
: 帖子以斜线打头的相对路径。page.date
: 帖子的日期。page.id
: 帖子的唯一标识码(在 RSS 源里非常有用)page.categories
: 这个帖子所属的 Categories。page.tags
: 这个 Post 所属的所有 tags。page.path
: Post 或者 Page 的源文件地址。
使用 Commentit 搭建 Blog 评论系统
由于 Github Pages 只支持纯静态的页面,所以我们无法使用数据库,这样简单的评论功能我们默认是无法实现的。但是感谢第三方评论系统,比如多说、disqus,由于它们,我们可以简单的实现静态网站的评论功能。我这里推荐使用 Comm(ent|it)。
Comm(ent|it) 评论的原理是:所有的评论都会变成 Git 的 Commit,push 到你的 Repository 里面去。具具体请移步 Comm(ent|it) 查看设置过程。