Jekyll博客的索引分页与静态API

立泉

Jekyll是一个从格式化文本生成静态站点的工具,所谓“静态”是指所有HTML页面都是在编译时创建,部署后不能动态生成新页面。由HTML组成的静态网站可以不需要传统后端服务,直接托管到免费的Github PagesCloudflare Pages上,非常适合博客之类的轻量用途。

但“静态”并不意味着网站不能交互,可以在页面里加载Js代码来监测交互事件动态改变显示内容,所以静态博客也能有非常大的灵活性。

索引分页

博客首页一般是文章索引,当文章数量很多时为优化加载速度和页面性能不应该一次加载整个列表,而是先加载一段,再按需逐段加载剩余内容,即Pagination“分页”。

常见的分页有2种,一种是“静态”,Jekyll编译时生成多个页面,每个页面只显示一部分,并在页面底部设置索引导航。这种方式非常简单,应用也最广泛,但我更倾向首页的文章列表是一条完整时间线,不应该拆分到多个页面,会破坏连续性。所以选择第二种“动态”,即监测用户滚动,用Js分段加载文章列表填充到页面显示出来,就像普通App那样。

如此一来需要让Jekyll在编译时生成分段的文章列表,每段都保存在单独的Json文件中,这些文件可以被Js下载解析,即是所谓的“静态API”。

静态API

Jekyll提供jekyll-paginate-v2插件来实现高级分页,它的Auto Pages模式可以自动给每一个CategoryCollectionTag都生成分页文件,整个站点的文章都被以不同方式分类、分页为可用的API

# Jekyll配置文件`_config.yml`
plugins:
  # 启用分页插件
  - jekyll-paginate-v2

# 配置分页参数
pagination:
  enabled: true
  # 要扫描的文章目录`_posts`
  collection: "posts"
  # 每页的文章数
  per_page: 10
  # 生成分页文件的路径,后面可以为Category、Tag、Collection单独配置
  permalink: ""
  # 分页文件后缀
  extension: json
  # 分页文件名,比如`page-2.json`
  indexpage: "page-:num"
  # 是否按时间倒序
  sort_reverse: true

# 配置自动分页
autopages:
  enabled: true
  # 禁用Category自动分页
  categories:
    enabled: false
  # 禁用Collection自动分页
  collections:
    enabled: false
  # 启用Tag自动分页
  tags:
    enabled: true
    # 每页的布局文件,定义生成什么样的Json
    # 指定为`_layouts/paginate-tag.html`
    layouts:
      - paginate-tag.html
    # 指定输出的Json文件路径
    permalink: "/api/paginate/tags/:tag"
    slugify:
      mode: "default"
      case: false

因为我博客里所有文章都是按Tag分类,所以只需配置Tag分页,不同的索引页再去加载对应TagJson

_layouts/paginate-tag.html里定义生成的Json格式,Jekyll会自动把每一页的文章列表填充到Liquid模版语言的paginator变量里,遍历paginator.posts生成的就是当页文章列表。

// _layouts/paginate-tag.html
// 用Liquid模版语言定义Json格式
{
    "data": {
        "totalPosts": {{ paginator.total_posts }},
        "totalPages": {{ paginator.total_pages }},
        "postsPerPage": {{ paginator.per_page }},
        "currentPageIndex": {{ paginator.page }},
        "previousPagePath": "{{ paginator.previous_page_path }}",
        "nextPagePath": "{{ paginator.next_page_path }}"
    },
    "posts": [
        {%- for post in paginator.posts %}
        {
            "title": "{{ post.title }}",
            "date": "{{ post.date | date: "%Y年%m月%d日" }}",
            "path": "{{ post.url }}",
            "author": "{{ post.author }}",
            "description": "{{ post.description}}",
            "cover": "{{ post.cover }}"
        }{% unless forloop.last %},{% endunless %}
        {%- endfor %}
    ]
}

注意_config.yml中的分页目录permalink: "/api/paginate/tags/:tag"和分页文件名indexpage: "page-:num",配置后执行jekyll build命令,会扫描_posts目录中的所有文章,在_site目录下生成按Tag分类的分页文件。

|-_site/api/paginate/tags/
    |-tag1/
        |-page-1.json
        |-page-2.json
    |-tag2/
        |-page-1.json
        |-page-2.json
        |-page-3.json

Js下载这些分页的Json文件,即可按需分段加载文章列表。

加载时机

滚动加载新数据是一个常见交互,如何检测滚动并判断加载时机也是一个常见话题,所以值得一并介绍。

首先要知道列表“滚动”的定义,它是指列表的Height高度超过其Viewport显示区域,所以处于可滚动的状态。此时只需检测列表的ScrollY滚动距离,就可以通过Height - Viewport.height - ScrollY计算列表的最后一个元素距离Viewport显示出来的距离,当它小于一个阈值就是触发加载新数据的时机。

另外,也要注意过滤掉滚动时高频触发的加载事件。

arrow_upward