文章

Jekyll:minima 自定义

minima 主题自定义教程:环境变量、导航栏、评论、Liquid raw、站内引用、TOC、标签页、时区、Google Analytics 与 SEO。

minima 是 Jekyll 默认主题,也是最简单的主题,很符合 Keep It Stupid and Simple 的原则。默认主题适合大众,但不一定完全适合自己的口味,所以理解一些原理,再增加自己想要的东西,很有必要。

  1. 自定义地图
  2. 环境变量
  3. 页面路径
  4. 自定义导航栏
  5. 评论系统
  6. 禁用 Liquid 模板渲染
  7. 站内引用
  8. 文章目录
  9. post 页面显示 tags
  10. 增加 tag/category 页面
  11. 自定义时区
  12. Google Analytics
  13. SEO
  14. 验收清单

自定义地图

minima 的自定义大致分三类:

flowchart TD
    A["minima 自定义"] --> B["配置型<br/>_config.yml / front matter"]
    A --> C["模板型<br/>_layouts / _includes"]
    A --> D["内容型<br/>tags.md / toc / links"]

    B --> B1["timezone"]
    B --> B2["google_analytics"]
    B --> B3["permalink"]
    C --> C1["导航栏"]
    C --> C2["评论区"]
    C --> C3["post 显示 tags"]
    D --> D1["站内引用"]
    D --> D2["文章目录"]
    D --> D3["tag/category 页面"]

教程化一点的顺序是:先改配置,再看模板,最后补内容页。

环境变量

Jekyll 支持环境变量,最常用的是 JEKYLL_ENV。默认是 development,生产环境通常设为 production

在 Liquid 中可以用 jekyll.environment 访问:

1
2
3
{% if jekyll.environment == "production" %}
  <script src="my-analytics-script.js"></script>
{% endif %}

本地模拟生产环境:

1
JEKYLL_ENV=production bundle exec jekyll serve

验证方式:打开页面源代码,确认只应该在线上出现的脚本是否被注入。

参考:

页面路径

页面路径有两种控制方式:

方式位置适合场景
permalink单篇 front matter单独指定某个页面路径
全局 permalink_config.yml统一配置 posts 路径规则

示例:

1
permalink: /:categories/:year/:month/:day/:title:output_ext

这意味着 categoriesdatetitle 都会影响最终 URL。路径设计尽量一开始想清楚,后面改 URL 会影响评论、搜索索引和外链。

更多规则看 Jekyll permalinks

自定义导航栏

minima 的导航栏逻辑在 header.html。核心逻辑是:

1
2
3
4
5
6
7
8
9
10
11
{%- assign default_paths = site.pages | map: "path" -%}
{%- assign page_paths = site.header_pages | default: default_paths -%}

{%- for path in page_paths -%}
  {%- assign my_page = site.pages | where: "path", path | first -%}
  {%- if my_page.title -%}
    <a class="page-link" href="{{ my_page.url | relative_url }}">
      {{ my_page.title | escape }}
    </a>
  {%- endif -%}
{%- endfor -%}

翻译一下:

flowchart TD
    A["site.header_pages 是否存在?"] -->|存在| B["只显示 header_pages 指定页面"]
    A -->|不存在| C["默认显示 site.pages 里的页面"]
    B --> D["按 path 找 page"]
    C --> D
    D --> E["有 title 才输出导航链接"]

如果不配置 site.header_pages,根目录里的页面可能都会出现在导航栏里,连 CHANGELOG.md 都可能混进来。要显式控制导航栏,在 _config.yml 中写:

1
2
3
header_pages:
  - about.md
  - index.md

验证方式:重启本地服务,看导航栏是否只剩指定页面。

参考:

评论系统

minima 默认支持 Disqus。思路是:静态页面本身不存评论,页面加载时根据 URL 去 Disqus 拉取对应评论。

sequenceDiagram
    participant Browser as 浏览器
    participant Page as 静态页面
    participant Disqus as Disqus
    Browser->>Page: 打开文章
    Page->>Disqus: 按 shortname + URL 请求评论
    Disqus-->>Page: 返回评论区

配置 _config.yml

1
2
disqus:
  shortname: <your-shortname>

模板里 include 评论区即可。可以参考 Disqus comments for Jekyll

在一个中文网站里接入了国外评论系统,应该注定我的网站是不会有人评论的吧……

踩坑点:

  • Disqus 按 URL 分组,改 URL 可能导致老评论对不上。
  • 测试域名不在可信列表时可能不显示,报错可参考 Disqus load failure 文档

禁用 Liquid 模板渲染

写 Jekyll 教程时经常要展示 Liquid 代码。问题是:代码块里的 Liquid tag 也可能被 Jekyll 先渲染掉。

Jekyll 4.0+ 可以在 front matter 中使用:

1
render_with_liquid: false

但这个粒度很粗。整篇文章都不渲染 Liquid 后,{% link %}{% post_url %} 这类站内引用也会失效。

更稳的做法是只包住代码片段:

1
2
3
{% raw %}
{{ page.title }}
{% endraw %}

但是如果这一篇文章本身就是介绍 Liquid 的 filter 和 tag,一个个加 raw...endraw,我快疯了……

参考 Liquid raw tagJekyll Liquid tags

站内引用

引用 posts 时可以用 post_url

1
[文章标题]({% post_url 2010-07-21-name-of-post %})

普通页面或 collection 文件可以用 link

1
[minima 结构]({% link _tutorials/2019-11-17-Jekyll-website.md %})

这样即使 permalink 改了,Jekyll 也能在构建时生成正确链接。更多看 Jekyll linking to posts

文章目录

Jekyll 默认使用 Kramdown 解析 Markdown。Kramdown 的 TOC 写法是:

1
2
1. Table of Contents, ordered
{:toc}

无序目录也可以:

1
2
* Table of Contents
{:toc}

如果某个标题不想进入目录:

1
2
# Header
{:.no_toc}

Table of Contents 这行文字会被目录替换,所以它写什么不重要。写 blabla 也行,只是以后看到会怀疑自己当时在干什么。

参考 Kramdown TOC

post 页面显示 tags

minima 的 post 模板默认主要显示 title、date、content。如果想在文章页展示 tags,可以覆盖主题模板。

新增或复制主题中的 _layouts/post.html,在日期附近加:

1
2
3
<div>
  {{ page.tags | join: ' | ' }}
</div>

这里用到了 Liquid 的 join filter

Jekyll 会优先使用项目本地的 _layouts/post.html,从而覆盖 gem 主题里的同路径文件。

增加 tag/category 页面

Jekyll 可以通过 site.tags 获取所有 tag。其结构可以理解为:

1
2
3
4
[
  [tagA, [postA1, postA2]],
  [tagB, [postB1, postB2]]
]

新增 tags.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
layout: page
title: Tag
permalink: /tag/
---

{% assign ordered_tags = site.tags | sort %}
{% for tag in ordered_tags %}
  <h3>{{ tag[0] }}</h3>
  <ul>
    {% for post in tag[1] %}
      <li><a href="{{ post.url }}">{{ post.title }}</a></li>
    {% endfor %}
  </ul>
{% endfor %}

关键点:

  1. site.tags | sort 先排序。
  2. tag[0] 是 tag 名。
  3. tag[1] 是该 tag 下的文章数组。

参考 Jekyll categories and tags

自定义时区

文章 front matter 中可以写:

1
date: 2019-12-10 02:11:29 +0800

这表示一个明确的时间点:东八区 2019-12-10 02:11:29。问题在于,显示时区还取决于构建机器或 _config.yml

如果 GitHub Pages 按 UTC 构建,可能显示成:

1
<time datetime="2019-12-09T18:11:29+00:00">Dec 9, 2019</time>

解决办法是在 _config.yml 中写:

1
timezone: Asia/Shanghai

带时区的 date 只是准确描述“同一刻”。最终按哪个时区展示,还要看 Jekyll 的 timezone 设置。

Google Analytics

Google Analytics 的本质是往页面里注入一段统计脚本。minima 已经准备了模板,只要配置 id:

1
google_analytics: <your-user-id>

通常 analytics 代码只在 JEKYLL_ENV=production 时注入,这样本地测试不会污染统计数据。

验证方式:

1
JEKYLL_ENV=production bundle exec jekyll serve

然后查看页面源代码,确认 analytics 脚本是否出现。

SEO

SEO(Search Engine Optimization)不是“给网页起个好标题”这么简单,但 meta 标签确实是基础。

网页的 <head> 中可以放大量 <meta> 标签,用来告诉搜索引擎:

  • 文章标题是什么。
  • 摘要是什么。
  • 作者是谁。
  • canonical URL 是什么。
  • Open Graph / Twitter Card 分享信息是什么。
  • 结构化数据是什么。

Jekyll 可以用 jekyll-seo-tag 自动生成这些标签。安装方式看 jekyll-seo-tag installation,生成内容看 jekyll-seo-tag usage

装完后查看页面源代码,应该能看到类似:

1
2
3
4
5
6
7
8
9
<!-- Begin Jekyll SEO tag -->
<title>文章标题 | 站点名</title>
<meta name="description" content="文章摘要" />
<meta property="og:title" content="文章标题" />
<meta property="og:url" content="http://localhost:4444/example/" />
<script type="application/ld+json">
{"@type":"BlogPosting","headline":"文章标题"}
</script>
<!-- End Jekyll SEO tag -->

更完整的 SEO 指南可以看 Google SEO Starter GuideGoogle structured data introduction

SEO 绝非加几个 meta 标签就结束,还包括网站结构、标题层级、图片 alt、页面速度、外链质量等等。真想推广网站,可以系统读一下 Google 的指南。

验收清单

改动怎么验证
导航栏重启 server,看页面顶部链接
评论生产环境构建,看评论区是否加载
Liquid 示例构建后代码块仍显示原始 Liquid
TOC页面内目录正常生成
tags 页面/tag/ 可访问并列出文章
时区文章日期和预期时区一致
SEO页面源代码中有 SEO meta

minima 虽然朴素,但拿它练习 Jekyll 自定义很合适。等这些概念都顺了,再换复杂主题时就不至于一上来就被模板、数据、路径、缓存混合暴打。

本文由作者按照 CC BY 4.0 进行授权