基于 Jekyll 实现博客文章「标签化」
立泉重构之后博客开始支持标签,每一篇文章都会被标记一些关键词作为内容侧重点,不同文章通过标记相同标签建立关联性。标签固定显示在文章顶部,点击会弹出包含相关文章的 Dialog,是非常好的写作分类机制。
Jekyll 驱动的静态站点借助 Liquid 模版语言实现标签并不复杂。
静态页面
首先在文章 Front Matter 里添加 Jekyll 原生支持的tags
,可添加多个,用空格隔开:
---
layout: post
categories: original
title: "基于Jekyll实现博客文章「标签化」"
author: 立泉
date: 2021-09-01 +0800
tags: Code Blog Jekyll GitHub Liquid
---
然后找到把 Markdown 文章转换成 HTML 页面的_layout
布局文件,用 Liquid 语言获取当前文章的tag
列表和标记该tag
的文章列表:
<!-- 遍历当前页面标记的 tag 列表 -->
{% for tagInPage in page.tags %}
<!-- 遍历整个站点的所有 tag 列表 -->
{% for tagInSite in site.tags %}
<!-- 从站点 tag 列表中找到当前页面的tag -->
<!-- 站点 tag 是一个数组,tag[0] 为该 tag 的名字,tag[1] 为标记该 tag 的所有文章列表 -->
{% if tagInPage == tagInSite[0] %}
<!-- 遍历标记此 tag 的所有文章列表 -->
{% for post in tagInSite[1] %}
<!-- 把文章列表填充到 HTML 中,点击 tag 弹窗显示 -->
{{ post.title }}
{{ post.date | date: "%Y.%m.%d" }}
{{ post.url | prepend: site.baseurl }}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
这样编译时标签和对应文章列表会被写入生成的静态 HTML 里,一般先隐藏,在合适的时机(比如点击标签时)以对话框形式弹出来。
但是当我接入 Google 搜索后发现,它在解析内容生成索引时会把页面内的文章列表误认为是文章内容,导致搜出一些完全不相干的文章,仅仅是因为它们都标记某个相同标签,而文章列表又恰好包含要搜索的关键字。所以直接嵌入页面不是一个好方案,而且模版语言 Liquid 相比真正的编程语言 JavaScript 在灵活性上也逊色很多。
动态加载
解决方法十分明确,不把文章列表写入静态页面而是在需要时动态加载,借助 Jekyll-Archive 插件可为每个tag
都生成一个包含相关文章列表的 HTML 文件,到时候读取它显示出来即可。
花一天时间做完 Push 到 GitHub Pages 上却没有生效,明明本地运行正常。搜索一圈知道并非所有 Jekyll 插件都能在 GitHub Pages 上使用,受支持的只是少部分,而Jekyll-Archive
并不在这个列表里。
插件是对 Jekyll 原生功能的封装,如果不要求为每个tag
生成独立的文章列表文件,而是写入同一个文件中,那么不借助插件 API 用普通遍历就可实现。
工程目录下创建api/tags.txt
文件,写入以下内容:
---
layout: none
permalink: /:path/:basename.json
---
{
"tags":[
{% comment %}遍历整个站点的所有 tag 列表{% endcomment %}
{%- for tag in site.tags %}
{
"tag":"{{ tag[0] }}",
"posts":[
{% comment %}遍历标记此 tag 的所有文章列表{% endcomment %}
{%- for post in tag[1] %}
{
"title": "{{ post.title }}",
"date": "{{ post.date | date: "%Y年%m月%d日" }}",
"url": "{{ post.url | prepend: site.baseurl }}"
}{% unless forloop.last %},{% endunless %}
{%- endfor %}
]
}{% unless forloop.last %},{% endunless %}
{%- endfor %}
]
}
依照 Liquid 语法,这段代码的作用是遍历所有tag
,将每个tag
和相关文章列表组合为 JSON 格式。如果以上配置正确,执行 Jekyll 的build
指令会把内容输出到_site/api/
目录的tags.json
文件中:
{
"tags": [
{
"tag": "标签_01",
"posts": [
{
"title": "标题_01",
"date": "日期_01",
"url": "url_01"
},
{
"title": "标题_02",
"date": "日期_02",
"url": "url_02"
}
]
},
{
"tag": "标签_02",
"posts": [
{
"title": "标题_01",
"date": "日期_01",
"url": "url_01"
},
{
"title": "标题_02",
"date": "日期_02",
"url": "url_02"
}
]
}
]
}
已有映射 JSON,点击tag
加载对应文章列表显示出来可逐步轻松实现。额…好吧,如果不熟悉 JS 并不会很“轻松”,比如我,感觉不“难”,思路清晰但是对语法和类库陌生,以至于编码步履蹒跚。
不过“久病成良医”,即使不系统性学习整个 Web 技术栈,长期从边角接触足以让我把这个博客站点的空想设计慢慢变为现实。