基于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文件,到时候读取它显示出来即可。

可是花一天时间做完PushGitHub 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格式。如果以上配置正确,执行Jekyllbuild指令会把内容输出到_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技术栈,长期从边角接触也足以让我把这个博客站点的空想设计慢慢变为现实。

arrow_upward