作为一个极简主义者,越来越受不了WordPress的臃肿。主要自己平时都是用Markdown来做记录,而WordPress支持的不是很好,另外有时写的md长文通过三方软件导出的html也不造放在wp的什么地方才合适,管理起来还不如几个静态页面来得方便。于是有了某天来个大整顿的想法。

对前端只停留在css的我,花了整三天时间,从Hexo到Jekyll再到Bootstrap,以及各种markdown2html解析器…最后基本算是从头写了一个基于Jekyll的website theme 😓 在此记录了各种前端坑,自认为还算详细吧。

Hexo

主流的静态页面生成器有俩,一个Jekyll,一个Hexo,都支持Markdown。 前者用的是Ruby,后者为Node.js。

Hexo会比Jekyll搭建起来更方便一些,而且各种配置项的设计也很便捷,网站themes还很丰富,所以我一开始弄的就是这个。

搭建方法上面这两个链接写的很详细。

这里主要记录一下让Hexo支持LaTex的注意地方。Hexo用MathJax时会有一些转义问题,经我自己测试,最好的方式是用hexo-renderer-pandoc渲染器代替原有的hexo-renderer-marked。而网上说的用hexo-renderer-kramed,对矩阵的情况显示的不好。

$ npm uninstall hexo-renderer-marked --save
$ npm install hexo-renderer-kramed --save

Jekyll

其实用Hexo/Jekyll+现成的主题,很方便就能搭出一个静态网站出来。只是除了代码高亮、LaTex这些功能之外,我还想能区分出code block 和普通无需highlighting的<pre>部分,还有其他一些改动。因为markdown解析的原因,基本所有themes出的效果都是将两者统一处理了。强迫症(其实就是zuo),主题挑来挑去都没找到个合心意的,比如next这种,太复杂,让我一个前端白痴改,哈,那还是自己从头写一个比较符合程序员造轮子的习惯吧。。。(捂脸

(至于为啥从Hexo转到了Jekyll,原因未明)

Get Started

参考:Jekyll Doc (中文版,内容比较旧)

Installation

本来在本地用Homebrew安装一路顺畅,但放到服务器就各种问题。

首先直接用apt-get下载的Ruby版本比较旧。

# Install Ruby & RubyGems
$ sudo apt-add-repository ppa:brightbox/ruby-ng
$ sudo apt-get update
$ sudo apt-get install ruby2.4

# Install Jekyll and Bundler gems
$ sudo gem install jekyll bundler

然后在gem install jekyll时会报错:

error: could not find a valid gem (>= 0) in any repository

参考github issues,F*k GWF,需要将https://rubygems.org/换成其他可访问的镜像,如Ruby China 镜像

$ gem sources --remove https://rubygems.org/
$ gem sources -a https://gems.ruby-china.org/
$ gem sources -l
*** CURRENT SOURCES ***
https://gems.ruby-china.org/   
# 确保只有 gems.ruby-china.org

重新尝试 install jekyll,还是报错:

current directory: /var/lib/gems/2.4.0/gems/ffi-1.9.18/ext/ffi_c
/usr/bin/ruby2.4 -r ./siteconf20170801-1836-1n75umz.rb extconf.rb
mkmf.rb can't find header files for ruby at /usr/lib/ruby/include/ruby.h

解决方式:

$ sudo apt-get install ruby2.4-dev

之后就可以正常安装jekyll了。

另外,根据Ruby China 镜像,你可以用 Bundler 的 Gem 源代码镜像命令

$ bundle config mirror.https://rubygems.org https://gems.ruby-china.org

这样就不用改你的 Gemfile 中的 source了。

source "https://rubygems.org"
...

Basic Command

用jekyll新建一个site:

$ jekyll new myblog
$ cd myblog
$ bundle exec jekyll serve

打开http://localhost:4000测试。

默认使用的是Minima theme. 目前基于jekyll 3.x的themes还比较少。 因为我是自己新建的theme,就不用这样方式了。


启动服务:

# 启动服务
$ jekyll serve

# 启动服务(gem-based theme)
$ bundle exec jekyll serve

# 脱离终端在后台运行
# 如果你想关闭服务器,可以使用`kill -9 1234`命令,"1234" 是进程号(PID)。
# 如果你找不到进程号,那么就用`ps aux | grep jekyll`命令来查看,然后关闭服务器。
$ jekyll serve --detach

# 和jekyll serve相同,但是会查看变更并且自动再生成。
$ jekyll serve --watch

生成静态页面(位于_site目录):

$ jekyll build

或者用:(watched for changes, and regenerated automatically.)

$ jekyll build --watch

Set up Your Site on VPS

网上清一色都是通过github pages来发布的站点,然后通过修改CNAME文件来达到绑定域名的目的。

我这里是放到VPS上,需要自己搭建web server环境。

Jekyll is a static site generator, not a webserver. You may generate the static files and serve with webserver like nginx, which provides such abilities.

参考:

因为是静态网站,所以Jekyll其实安装在本地就可以,静态页面也放在本地。然后将Jekyll生成的静态HTML文件(_site目录下)通过类似FTP的方式上传到VPS就行。

将本地数据上传到远程服务用scp就行,后来了解到rsync这个命令。rsync只传送两个文件的不同部分,而不是每次都整份传送,因此速度很快。

先来理清关系:

Nginx

sudo apt-get install nginx
sudo service nginx start

然后输入vps地址就能看到nginx的欢迎页面。

查看运行状态:

sudo service nginx status
# 显示:
- nginx is running

修改 nginx 配置:sudo vim /etc/nginx/sites-enabled/default

root /usr/share/nginx/html; 注释掉,改为 root /home/deploy/your_blog_name/_site

server_name localhost; 注释掉,改为 server_name your_domain.com;

重新启动nginx,就能看到建的blog内容了。

Capistrano

Capistrano 是一个 Ruby 程序,参考第一个文章链接,它可以通过Git复制代码到服务器等操作。

sudo gem install capistrano 

这里先存留吧,暂时还没用到Capistrano。

目前是按照利用Apache进行多站点配置设置的。

Create New Theme From Scratch

既然是from scratch,就先把jekyll的目录结构理清,然后借助bootstrap框架创建主题,最后就是各种features了。

Basic

Directory Structure

jekyll new-theme xxx命令,就可以创建出一个theme需要的基本目录项。

.
├── _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
├── _data
|   └── members.yml
├── _site
└── index.html

说明:

_config.yml 是配置文件。默认配置参见:https://jekyllrb.com/docs/configuration/#default-configuration

_includes 里的文件为了布局重用。

_layouts 站点布局模板。布局可以在 YAML 头信息中根据不同文章进行选择。

_posts 将md文件以2017-08-01-welcome-to-jekyll.md的格式放到该目录中。

_drafts 中的md文件直接以title.md命名,并不会发布出来。当运行jekyll serve或者jekyll build --drafts时,草稿文章会被加上日期值并发布出来。

_site 存放jekyll转化完成的html文件。有时修改效果不生效,可以尝试删除该目录。

Variables

自带的变量详见:Variables

或者通过_config.yml添加。

Need to Know

若在index.html的YAML头信息中加入:

---
layout: default
...
---

则在default.html中调用{{ content }}时,就会把index.html中的所有内容放到调用处。

Bootstrap

Bootstrap前端框架真是我这种前端白痴的福音。

下载Bootstrp,我这里将解压后的文件放到了assets/bootstrap-3.3.7-dist 目录下。

使用Bootstrap:

参考Bootstrap官网里给的基本HTML模板

(1)将下面这行代码改成自己的相应路径后,放到head.html<head></head> 标签中。

<!-- Bootstrap -->
<link rel="stylesheet" href="/assets/bootstrap-3.3.7-dist/css/bootstrap.min.css">

(2)将下两行改成自己的相应路径后,放到default.html<body></body> 标签中的最下方,为了最后加载。

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="/assets/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>

若没效果,删除 _site 目录再试试,或者用命令重新 build。

参考Bootstrap官网给的示例代码,写入header.html文件中。

然后在default.html<body></body>标签中引入{% include header.html %}

注意的点是,

若使用了.navbar-fixed-top 类,这个固定的导航条会遮住页面上的其它内容,除非你给 <body> 元素底部设置了 padding,例如:

body { padding-top: 70px; }

Features

Post Excerpt

index.html 中显示文章摘要。参见https://jekyllrb.com/docs/posts/#post-excerpts

(1)自带的{{ post.excerpt }} 会自动取第一段的内容作为摘要。

若想自定义,

(2)在_config.yml 文件中指定摘要的分隔符:

excerpt_separator: <!-- more -->

这样会覆盖自带的{{ post.excerpt }}功能,然后在文章内容需要分隔的摘要后面加上<!-- more -->。而<!-- more --> 本身作为注释,不会影响 markdown 的显示。

(3)或者采用这样的方式:

{{ post.content | strip_html | truncatewords:75 }}

Pagination

用于index.html 里对文章的分页显示功能。

(1)首先安装jekyll-paginate

$ sudo gem install jekyll-paginate

在配置文件_config.yml中添加:

plugins:
  - jekyll-paginate
paginate: 5
paginate_path: "/pages:num/"

其中,paginate: 5 设置的是分页数; paginate_path: "/pages:num/" 设置的是URL的显示格式,如 http://localhost:4000/pages2/。

(2)然后参考https://jekyllrb.com/docs/pagination/index.html添加分页功能,并结合Bootstrap里的分页样式修改:http://getbootstrap.com/components/#pagination

这里利用了 bootstrap 的栅格布局,栅格布局将一个页面分割成12个等宽的列。(详见 example)

<div class="row-fluid">
  <div class="col-md-9">
    <h2>Content</h2>
  </div>
  <div class="col-md-3">
    <h2>Sidebar</h2>  
  </div>
</div>

让侧边栏固定住,不随页面滚动而滚动:加入class="affix"


说明:

我这里设置两种sidebar:

  • 一种是index.html里的,用于显示author信息,写在main_sidebar.html中;
  • 另一种是post对应的,用于显示目录列表,写在post_sidebar.html中。

Social Icons

icons用的是 fontawesome。我这里将解压后的 fontscss文件夹复制到了assets目录下。

head.html中添加:

<link rel="stylesheet" href="/assets/css/font-awesome.min.css">

要使用哪个图标,直接去官网搜代码就行。

修改图标颜色,因为其本身就是一种字体,故使用color 修改即可。

修改图标大小也是,用font-size即可,或者:

<i class="fa fa-camera-retro"></i>
<i class="fa fa-camera-retro fa-2x"></i>
<i class="fa fa-camera-retro fa-3x"></i>

对官网搜不到的社交网站图标怎么办,如“知乎”的:

<a href="https://www.zhihu.com/people/lszero" class="btn" title="Zhihu"><span class="fa-stack fa-lg"><i class="fa fa-circle fa-stack-2x"></i><i class="fa fa-stack-1x fa-inverse"></i></span></a>

Table of Content

post中显示目录

kramdown自带了解析目录的功能。详见:https://kramdown.gettalong.org/converter/html.html#toc

在md文件中加入:

# 无序列表
* TOC
{:toc}
{: .this-is-my-class}

或者:

# 有序列表
1. TOC
{:toc}
{: .this-is-my-class}

并且可在_config.yml中设置要显示的标题级别:

kramdown: 
  toc_levels: "2,3" 

sidebar中显示目录

侧边栏目录随浏览内容动态滚动功能,利用Bootstrap的Scrollspy来实现。

最后写了个 TOP.js 终于实现得还算满意了。(当然,还要配合 css 使用)

另外,其它不错的实现:

Hide sidebar for mobile display

手机显示时,隐藏侧边栏,否则会显示混乱。

@media (max-width: 800px) {
    #sidebar {
        display:none !important;
    }
}

参考:Button addons & Forms

将下面代码加入header.html中:

<form class="navbar-form navbar-left" role="search">
  <div class="input-group">
    <span class="input-group-btn">
      <button class="btn btn-default" type="button">Go!</button>
    </span>
    <input type="text" class="form-control" placeholder="Search">
  </div>
</form>

对于search逻辑,目前还没找到一个很好的方式去实现。。。(如果知道的,麻烦告诉我一声!)

Back to Top

该功能我放在了post_sidebar.html中:

<a href="#top" class="back-to-top">^</a>

其实就是个link,最后再用css设置样式。


更新:

为了不让footer覆盖掉 back-to-top,只能把back-to-top写在footer后面。故改在了default.html里。

Comments

Gitment

Gitment 挺不错的的。按照官网的说明引入js后,在其中修改自己的OAuth Application信息。

isso

这是自建评论系统的一种方式。官网需要fq.

我之后用的也是这种方法,详见:搭建Isso评论服务

Archives

{% for post in paginator.posts %} 并不能使用,因为paginator只会对index.html有效。

site.posts | size 获取总的文章数。

因为jekyll不支持按日期获取,只能暂时用{% for post in site.posts %}

这部分待补充吧。

Category

参考 使用Category分类

首先在每个文章的YAML头信息中定义类别,如:

---
layout: post
title:  "your title"
categories: Algorithm
---

获取所有类别:

{% for category in site.categories %}
  <h2>{{ category[0] }} ({{ category[1].size }})</h2>
  <ul>
  {% for post in category[1] %}
    <li>{{ post.date | date:"%d/%m/%Y"}}<a href="{{ post.url }}">{{ post.title }}</a></li>
  {% endfor %}
  </ul>
{% endfor %}

使用:

  • site.categories.size 获取总的类别数。
  • category[0] 获取分类名称。
  • category[1].size 获取该分类下文章的数目。
  • site.categories.CATEGORY 可得到某个特定类别的所有文章。

更多变量可参见:http://jekyllrb.com/docs/variables/#site-variables.

输出单个分类下的所有文章:

site.categories.CATEGORY 需要手动指定,并不是很方便。

使用Category分类这篇文章里使用js来实现。

我这里是先生成好categories.html,里面列出了所有的文章。然后利用锚点categories.html#xxx进行跳转到指定的类别。

Tags

参考使用文章标签索引文章

首先在每个文章的YAML头信息中定义tags,如:

---
layout: post
title:  "your title"
tags: [github, jekyll]
---

category的设置方法差不多,就不赘述了。

Latex

参考:https://jekyllrb.com/docs/extras/#math-support

我这里为了避免所有pages都引入mathjax相关js代码,采用了不同的设置:

在配置文件_config.yml中添加:

mathjax: false

head.html中添加:

{% if page.mathjax %}
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
{% endif %}

然后在需要引入mathjax的md文件头中加入mathjax: true,如:

---
layout: post
...
mathjax: true
---

Support $

默认只支持双$符,修改:

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    TeX: {
      equationNumbers: {
        autoNumber: "AMS"
      }
    },
    tex2jax: {
      inlineMath: [ ['$','$'] ],
      displayMath: [ ['$$','$$'] ],
      processEscapes: true,
    }
  });
</script>

Mathjax Rendering Problem

若latex公式中包含|符,如|a|,会被markdown解析器当成<table></table>来处理。

又如,定义矩阵时,其中的换行\\也会被解析出问题,把第一个\当成了转义符。

解决方式:

对第一种情况,根据 Syntax | Kramdown 上的说明,将|\vert来代替。

对第二种情况,用双$$符解决转义问题。

Code Highlighting

Jekyll 3.x 自带了rouge代码高亮的功能,但是不造为啥一直not working(难道又被看脸了。。

于是这里用的是highlightjs。在head.html里添加:

<link rel="stylesheet" href="/lib/highlight/styles/hybrid.css">
<script src="/lib/highlight/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

我这里把下载的highlightjs的相关文件放在了lib目录下,样式选的是hybrid.css。(monokai-sublime 和 hybrid 配色也不错。)

highlightjs会解析并高亮<pre><code>...</code></pre>中的内容。如

<pre><code class="html">...</code></pre>

若不需要高亮则设为:

<pre><code class="nohighlight">...</code></pre>

这里有个麻烦的地方,若md里没有指明语言,highlightjs也会自动检测是什么语言。如果不想对某个code block进行高亮,有两种解决方式:(当然了,我比较折腾,一般人也不需要这个。。(捂脸)

(1)每次手动加入nohighlight

​```nohighlight
xxx
​```

(2)修改markdown解析器。

第一种方式比较麻烦,但highlightjs又没有提供关闭highlightAuto的接口。

Custom Markdown Parser

_config.yml中可以看到,默认使用的是kramdown解析器。

markdown: kramdown

关于Custom Markdown Processors官网有一些说明,好吧,没看懂,毕竟不会Ruby。kramdown也不能以overwrited的方式改写相应的convert方法,然后又写了段js代码试图修改kramdown解析出的html,但还是没成功。真是艰辛,网上找了好久都无果,最后还不如直接看代码改来得快。。。

好在highlightjs提供了这个:

// ignore languages
<script>hljs.configure({ ignore: ['text'] });</script>

然后看Jekyll源码,找到了 jekyll/lib/jekyll/converters/markdown/kramdown_parser.rb (see code)

def convert(content)
  Kramdown::Document.new(content, @config).to_html
end

接着看kramdown源码,找到了kramdown/lib/kramdown/converter/html.rb (see code)

在我的本地电脑中,相关文件位于/usr/local/lib/ruby/gems/2.4.0/gems/kramdown-1.14.0/lib/kramdown/converter/html.rb

找到def convert_codeblock(el, indent)函数进行修改:

在代码code_attr['class'] = "language-#{lang}" if lang 前添加:

if lang.nil?
  lang = "text"
end

并将代码if highlighted_code 改为 if highlighted_code && lang

(其中,nil是判空的意思。)

这样在不指明语言的时候,不高亮显示。


上面是mac环境下,可以直接改代码。但是在linux环境下,只有一个二进制执行文件。

于是,先删除原有的:

$ sudo gem uninstall kramdown

然后在官网下载源码重新安装:

$ ruby setup.rb config
$ ruby setup.rb setup
$ ruby setup.rb install

好吧,暂时没弄好。。。

Collections

参见:

Else

_config.yml中设置,如:

permalink: /:categories/:year/:month/:day/:title.html

当页面内容长度不够时,footer显示位置可能有问题。

让footer内容永远处于最末端:

$height-footer: 40px;

html {
    height: 100%;
}

body {
    min-height: 100%;
    position: relative;
    padding-bottom: $height-footer;
}

.footer {
    bottom: 0;
    width: 100%;
    position: absolute;
    height: $height-footer;
}

子元素的百分比高度也可以基于父元素的百分比高度,前提是父元素的父元素必须有一个明确的高度。要使min-height的百分比值生效,其父元素的height值必须为一个固定的高度或者是一个有效的百分比高度。

Problems

{{ xxx }} 或者 {% xxx %} 显示不出来。

解决方式:

(1)

\{\{ xxx \}\}

(2)参考:Escaping double curly braces inside a markdown code block in Jekyll

{% raw %}    
This is a test: {{ xxx }}
{% endraw %}

(3)那么问题又来了,如何显示上面的 raw 和 endraw 呢?参考:jekyll 如何转义字符

server {
    listen [::]:80;
    server_name comments.lszero.com;
    
    location /isso {
        proxy_pass http://localhost:8090;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Script-Name /isso;
    }
}