CryptOChicken

Hexo Blog

Table of Contents

如您在页脚处所见, 本站是由 Emacs Org mode 生成的, 而不是 Hexo. Hexo 陪伴了我很长一段时间, 但自从入手 Org mode 之后我就不再使用 Hexo 了, 关于 Hexo 的事情就不应该再归在 Site Config Doc 里了. 但 Hexo 的确很便于初学者搭建博客, 实际上, 本站的主体配色就基本上来自 HexoNexT.Pisces (NexT 主题的 Pisces scheme), 遂把之前写过的内容迁移到此以表感激之情.

1. Prerequisite

sudo pacman -S npm
sudo npm install -g hexo-cli

2. Basics

2.1. Init

mkdir Blog && cd Blog
hexo init
npm install                    # install dependency

2.2. Essential modules

通过npm安装的模块都位于./nodemodules/:

npm list
npm install --save hexo-deployer-git
npm install --save hexo-math
npm install --save hexo-blog-encrypt
npm install --save hexo-renderer-org  # If use Emacs Org mode
npm uninstall xxx

2.3. ./_config.yml

要注意yml文件的 缩进. 关于如何同时推到两个git repo的写法, 参考hexojs/hexo-deployer-git: Git deployer plugin for Hexo.

# Site
title: OChicken #Hexo
author: Shouran Ma #John Doe
timezone: Europe/Berlin #''

# URL
url: https://ochicken.top #http://example.com

# Writing
post_asset_folder: true

# Extensions
theme: next #landscape

math:
  engine: 'mathjax' # or 'katex'
  mathjax:
    src: #custom_mathjax_source
    config:
      # MathJax config
  katex:
    css: custom_css_source
    js: custom_js_source # not used
    config:
      # KaTeX config

## Security
encrypt:
    enable: true
    silent: true

# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
- type: git
  repo: [email protected]:OChicken/OChicken.github.io.git
  branch: master
- type: git
  repo: [email protected]:OChicken/OChicken.DigitalOcean.io.git
  branch: master

2.4. Usage

hexo clean                    # 清空冗余文件
hexo g (= hexo generate)      # 生成网页
hexo s (= hexo server)        # 浏览器里输入 http://localhost:4000/, 就可以看到刚才的成果了
Ctrl+C                        # 结束
hexo d (= hexo deploy)        # 部署到云

顺带一提: "博客" 这个词给人第一感觉是托管在 " 服务器" 里的. 实际上如前面提到的, "云" 这个前缀只不过是有一个独立IP可供世界各地的人访问而已; 如果你只想自个儿看, 那在一个甚至不需要联网的 PC 里都可以完成, 这个东西就是 http://localhost, 本地主机.

3. Theme NexT: Basic

Hexo官方主题合集: Themes | Hexo

Hexo下载的是默认的 landscape 主题. 当然可以换别的. 最常用的是 NexT, 其目录位于 ./theme/next/.

3.1. Install

这是有效的那个版本:

git clone https://github.com/theme-next/hexo-theme-next.git themes/next

NexT还有别的版本, 含原始版本和衍生版本, 谨记录在此, 不保证有用:

git clone https://github.com/iissnan/hexo-theme-next.git theme/next      # 原始版本
git clone https://github.com/next-theme/hexo-theme-next.git themes/next  # 衍生版本
npm install hexo-theme-next                                              # 衍生版本, 通过npm安装

虽然 ./theme/next/_config.yml./_config.yml 同名, 但 都掌管着各自的前端, 不可随意替换.

本节的内容主要是编辑 ./theme/next/_config.yml.

3.2. Avatar

Setup ./images/Cola.png and ./images/wechatchannel.jpg.

favicon:
  small: /images/favicon-16x16-next.png
  medium: /images/favicon-32x32-next.png
  small: /images/Cola.png #favicon-16x16-next.png
  medium: /images/Cola.png #favicon-32x32-next.png
  apple_touch_icon: /images/apple-touch-icon-next.png
  safari_pinned_tab: /images/logo.svg
  #android_manifest: /images/manifest.json

footer:
  # Specify the date when the site was setup. If not defined, current year will be used.
  #since: 2015

  # Icon between year and copyright info.
  icon:
    # ...
    # Change the color of icon, using Hex Code.
    color: "#808080"  #"#ff0000"

  # If not defined, `author` from Hexo `_config.yml` will be used.
  copyright: "OChicken"

# ...

# Schemes
#scheme: Muse
#scheme: Mist
scheme: Pisces
#scheme: Gemini

# Sidebar Avatar
avatar:
  # Replace the default image and set the url here.
  url: /images/Cola.png  #/images/avatar.gif
  # If true, the avatar will be dispalyed in circle.
  rounded: false
  # If true, the avatar will be rotated with the cursor.
  rotated: false

# Posts / Categories / Tags in sidebar.
site_state: false #true
# Usage: `Key: /link/ || icon`
# Key is the name of menu item. If the translation for this item is available, the translated text will be loaded, otherwise the Key name will be used. Key is case-senstive.
# Value before `||` delimiter is the target link, value after `||` delimiter is the name of Font Awesome icon.
# When running the site in a subdirectory (e.g. yoursite.com/blog), remove the leading slash from link value (/archives -> archives).
# External url should start with http:// or https://
menu:
  home: / || fa fa-home
  #about: /about/ || fa fa-user
  #tags: /tags/ || fa fa-tags
  #categories: /categories/ || fa fa-th
  #archives: /archives/ || fa fa-archive
  #schedule: /schedule/ || fa fa-calendar
  #sitemap: /sitemap.xml || fa fa-sitemap
  #commonweal: /404/ || fa fa-heartbeat
  Research: /research/ || fa fa-flask
  Personal Projects: /projects/ || fa fa-lightbulb
  Footprint: /footprint/ || fa fa-paw
  Library: /library/ || fa fa-book

3.5. Math

# Math Formulas Render Support
math:
  # Default (true) will load mathjax / katex script on demand.
  # That is it only render those page which has `mathjax: true` in Front-matter.
  # If you set it to false, it will load mathjax / katex srcipt EVERY PAGE.
  per_page: true

  # hexo-renderer-pandoc (or hexo-renderer-kramed) required for full MathJax support.
  mathjax:
    enable: true #false
    # See: https://mhchem.github.io/MathJax-mhchem/
    mhchem: false

3.6. 访客统计

# Show Views / Visitors of the website / page with busuanzi.
# Get more information on http://ibruce.info/2015/04/04/busuanzi
busuanzi_count:
  enable: true #false

4. Theme NexT: Advanced styles

本节编辑的文件都是 ./source/css/main.styl. (它的 syntax 实际上就是一个 CSS 文件)

4.1. Line height

This is a patch to ./source/css/_variables/base.styl. Change $line-height-base from 2 to 1.5:

// Variables Layer
// --------------------------------------------------
for $variable in $variables
  @import "_variables/" + $variable;
for $inject_variable in hexo-config('injects.variable')
  @import $inject_variable;
$line-height-base         = 1.5;             // patch

4.2. Common Scaffolding

This is a patch to ./source/css/_common/scaffolding/base.styl, aiming to modify headers (h1-6), horizontal rule (hr), paragraph (p):

// Scaffolding
@import "_common/scaffolding";
h1, h2, h3, h4, h5, h6 {                     // patch
  line-height: 1.0;                          // patch
  margin: 15px 0 15px;                       // patch
}                                            // patch
hr {                                         // patch
  margin: 15px 0;                            // patch
}                                            // patch
p {                                          // patch
  margin: 0 0 10px 0;                        // patch
}                                            // patch

4.3. Common Layout

This is a patch to ./source/css/_common/outline/sidebar/sidebar-author-links.styl. 默认的social links是居中的, 当某个link名字特别长的时候就不对齐. 此设置把text-align设为左对齐以缓解此问题.

// Layout
@import "_common/outline";
.links-of-author a, span.exturl {            // patch
  text-align: left;                          // patch
}                                            // patch

4.4. Common Components - Global Font-size

This is a patch to ./source/css/_common/components/post/post.styl. Set global font-size in post:

// Components
@import "_common/components";
.post-body {                                 // patch
  font-size: $font-size-medium;              // patch
}                                            // patch

当时想过在 ./_config.yml 设置 font:, 但是这样子搞, 会把post正文, 连同sidebar和toc的字体都改小. toc的字体再小一号就太小了, 遂不得不在./source/css/main.styl里手动改.

4.5. Common Components - Unordered List

This is a patch to ./source/css/_common/components/post/post.styl. 写文档的时候总会出现这种用语情境: "其原因有: xxx, yyy, …" 如是分点列出. 我们发现, 经Hexo渲染之后, 无序表 ("xxx, yyy, …") 跟它的前一段 ("其原因有:") 总是隔着很大margin. 为此我做了如下设置:

@import "_common/components";
.post-body ul {                              // patch
  margin-top: -10px;                         // patch
  margin-bottom: 0;                          // patch
}                                            // patch

现在解释一下为何这么设置. 前面我设置了段落 p 的格式, margin-top为0, margin-bottom为10px. ul 也是 p 的一种, 即无序表的margin-top为0, margin-bottom为10px. 段落 "其原因有:" 跟无序表 "xxx, yyy, …" 被视为了两个段落 (就行文逻辑而言它们应该是一段), 这两个段落之间的margin是 "其原因有:" 这个段落的margin-bottom造成的. 所以, 需要把 ul 的margin-top设为-10px, 抵销掉 这个margin, 这样二者就看起来是一段的了.

另一方面, ul 的margin-bottom设为0, 跟 p 的margin-top为0相适配, 表明它们之间没有margin. 这也符合实际: 假如列完无序表后还有一些补充 (这也应当视为一段), 那么紧跟着写就是了, 中间不会有任何margin; 如果是新开一个段落, 那么就加个空行.

须注意, 此设置仅用于Hexo在网页前端的渲染, 其html文件里依然是有空行的 — 换句话说, 是Hexo前端欺骗了你. 如果你用pandoc或Emacs Org将本文件生成为html, 再用Emacs-EWW打开, 会发现它们中间的确是空了一行, 只是再经过Hexo的样式渲染, 才看到浏览器上显示的结果.

4.6. Images Container

这个功能相当于LaTeX的float环境; 在css里, 使用flex来作为图片的container, 中间用hfill填充, 图片默认宽度为linewidth的25%.

.image-container {                           // patch
  display: flex;                             // patch
  justify-content: space-between;            // patch
}                                            // patch
.image {                                     // patch
  width: 50%;                                // patch
}                                            // patch

4.7. 加密后不显示目录

Ref: Hexo博客文章加密 | IT范儿

打开 ./layout/_macro/sidebar.swig, 把这个block

{%- if display_toc %}
  {%- set toc = toc(page.content, { class: "nav", list_number: page.toc.number, max_depth: page.toc.max_depth }) %}
  {%- set display_toc = toc.length > 1 and display_toc %}
{%- endif %}

的第2行改为

{%- if (page.encrypt) %}
  {%- set toc = toc(page.origin, { class: "nav", list_number: page.toc.number, max_depth: page.toc.max_depth }) %}
{%- else %}
  {%- set toc = toc(page.content, { class: "nav", list_number: page.toc.number, max_depth: page.toc.max_depth }) %}
{%- endif %}

以及把这个block

<div class="post-toc-wrap sidebar-panel">
  {%- if display_toc %}
    <div class="post-toc motion-element">{{ toc }}</div>
  {%- endif %}
</div>

第1,2行之间插入这一块

{%- if (page.encrypt) %}
  <div id="toc-div" style="display:none">
{%- endif %}

5. Deploy

很多关于搭 Hexo 相关的博客都提到, 要搞定发布博客到云服务器的这一步, 须自己在云服务器搭一个 git repo. 我认为自建 git repo 的难度远高于去配置 Hexo 博客本身, 这两个放一起, 对初学者是很不友好的, 除非你有像我当年一样一连两个月去自己捣鼓这个.

实际上 要做 deploy, 你其实可以不那么折腾. hexo g 之后, 直接把生成出来的 public/ 目录里的东西一股脑地 上传/var/www/html/ 就行了, 用过 scp 的读者都应该知道这真的是小事, 再不济, 如果你用 Windows 的话, 你还有 WinScp. 自建 git repo 的作用恐怕在于, 便于你像 hexo d 到 GitHub 那样去 hexo d 到自己的 VPS, 兜兜转转一大圈, 竟然只是为了用 hexo d 去 deploy! 我不知道为什么那么多 Hexo 相关的博客谈及 deploy 都说要自己搭 git repo (至少我印象中) …

当然如果你知道怎么自建 git repo, 那也不亏, Your patient mastery of this is well-rewarded. I assure you.

Refs:

5.1. 远程git仓库

GitHub 就是一类天然带了 git 仓库的 VPS, 只不过是我们没法 shell 登录, 也不知道专属的 IP 是多少; 自己购买的 VPS 并不像 GitHub 那样有 git 仓库, 所以得自己配置. 在VPS上自建git仓库 来托管代码就是重点要讨论的主题.

Hexo 个人博客的关键一步是 在VPS上创建git仓库.

5.1.1. 创建git用户

使用如下命令创建git用户. -m 表示创建用户目录/home/git, -G sudo 表示用户git除了隶属于 git 这个用户组以外还隶属于 sudo; -s /usr/bin/git-shell 表示login shell为git-shell.

useradd -m -G sudo -s /usr/bin/git-shell git

login shell不是/bin/bash, 因为须禁止git以shell登录.

5.1.2. 给git配置公钥

sudo su  # now you are root
mkdir -p /home/git/.ssh
cp /home/OChicken/.ssh/authorized_keys /home/git/.ssh/

给git用户配置公钥并不是为了用来登录, 而是发布博客时, 本地 hexo d 就直接 免密git push 到云服务器了, 就像我们通常使用git push一样.

5.1.3. Blog.git

在继续之前先理解下面几个写法:

git clone [email protected]:OChicken/Blog.git
scp       [email protected]:OChicken/Blog.git  ./
git clone [email protected]:Pigcatdog/Blog.git
scp       [email protected]:PigCatDog/Blog.git ./

git clone和scp的功能是非常相似的, 都可以把云上的一个目录”下载”下来到本地. 可是, OChicken和PigCatDog是github.com上的两个不同的用户, 为什么scp的时候都是[email protected]呢? 就ssh的经验来讲, 不应该是[email protected], [email protected]吗?

其实, github.com的服务器上只有一个用户: git, 其 HOME=/home/git/. 每注册一个用户, 就在$HOME下创建一个目录: $HOME/OChicken, $HOME/User1, HOME/User2, … 我OChicken每创建一个repo, 其在github.com上的路径就是 $HOME/OChicken/Blog.git, $HOME/OChicken/Work.git, … 当我在本地克隆时, git clone的写法就跟scp的写法非常相似了.

如果说在GitHub上用的是 git clone [email protected]:OChicken/Blog.git, 那么在OChicken.net上的情形就应该是 git clone [email protected]:OChicken/Blog.git. 为此

cd /home/git
mkdir -p OChicken && cd OChicken
git init --bare Blog.git

Blog.git的内容是这样的:

branches  config  description  HEAD  hooks  info  objects  refs

这是empty Git repo的特征. 尝试在本地 git clone [email protected]:OChicken/Blog.git, 并git add commit push三连, 其效果跟你在github.com上操作是完全一样的, 这表明我们确实在OChicken.net上创建了个git仓库, 只是域名从github.com换成了OChicken.net而已.

注: git init --bare 是指创建bare repo, 必须要有个 --bare, 不然一定报错. 理由详见Git Push error: refusing to update checked out branch - Stack Overflow

5.1.4. 配置git hooks, 指向 /var/www/html/

到此步之前, 所建立的远程git仓库都还仅局限在自己的小天地. 我们希望Blog.git的内容能open to the public, 也就是出现在 /var/www/html/. 为此须编辑git hooks:

cd /home/git/OChicken/Blog.git/hooks
touch post-receive
chmod 755 post-receive
vim post-receive

输入如下内容后保存退出.

#!/bin/bash
GIT_REPO=/home/git/OChicken/Blog.git
TMP_GIT_CLONE=/tmp/blog
PUBLIC_WWW=/var/www/html
rm -rf ${TMP_GIT_CLONE}
cd ${PUBLIC_WWW}
find . -type f ! -path "./library/*" -delete
cd -
git clone $GIT_REPO $TMP_GIT_CLONE
cp -rf ${TMP_GIT_CLONE}/* ${PUBLIC_WWW}

这表示, 每次git push (其实是hexo d) 后, 都会把Blog.git的内容复制到 /var/www/html/.

5.1.5. 收尾: 修改所有者

别忘了修改读取权限和所有者

chown -R git:git /home/git/
chown -R git:git /var/www/html/

作为测试, 再来一次git add commit push三连, 应该能够在 /var/www/html/ 看到non-trivial的内容.

5.2. 本地配置

这里只谈Hexo Blog本地配置涉及deploy到服务器的部分. 在博客根目录的 _config.yml:

# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
- type: git
  repo: [email protected]:OChicken/OChicken.github.io.git
  branch: master
- type: git
  repo: [email protected]:OChicken/Blog.git
  branch: master

然后使用 hexo d 部署. 关于Hexo如何配置和使用, 请看上一节.