基于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都生成相关文章列表:

<!-- 遍历当前页面标记的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搜索后却发现,它在解析内容生成索引时会把页面内的那个文章列表误认为是文章内容,导致搜出一些完全不相干的文章,仅仅是因为它们都标记了某个相同的标签,而文章列表里又恰好包含要搜索的关键字。所以静态页面虽然简单但伴随着很多问题,生成HTML页面的模版语言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文件,内容就是上面输出的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