在Hexo中创建带分页的自定义模板页面

一、背景

最近新上了“天工”模块,打算分享下个人见到的一些好玩的物件(Ps:第一个发布的就是上周在WAIC展会上看到的无人做饭灶)。顺便也分享下第一次开发带分页的模板开发经验。

“天工”一词取于明代著作《天工开物》,它是世界上第一部关于农业和手工业生产的综合性著作,记载了明朝中叶以前中国古代的各项技术,被外国学者称它为“中国17世纪的工艺百科全书”。

在过去的“造物”、“收藏”、“足迹”等板块,因为其自身内容较少,导致单页面已经足够满足使用场景。而“天工”版块随着时间推移,分享的物件也会越来越多,于是就需要提前准备好分页功能

二、必备插件 hexo-pagination

在官网的API文档中,关于生成器 (Generator)模块的文档中有提到以下内容:

有分页的归档页面
您可以通过 hexo-pagination 这个方便的官方工具来轻松建立分页归档。

hexo-pagination就是我们创建多分页模板的核心,下面是官方提供的归档模块(archive)示例代码:

1
2
3
4
5
6
7
8
9
10
var pagination = require("hexo-pagination");

hexo.extend.generator.register("archive", function (locals) {
// hexo-pagination makes an index.html for the /archives route
return pagination("archives", locals.posts, {
perPage: 10,
layout: ["archive", "index"],
data: {},
});
});

可以看到生成功能主要是由pagination提供,它接受三个参数:

  1. “archives”:代表生成目录的路径,此处代表生成的路径就是/archives/***
  2. locals.posts:分页的数据来源。locals是hexo的内部变量,类似于模板内的site变量。通过locals.posts可以获取到全部的文章内容。
  3. { xxx: xxx }:自定义配置项,主要由以下几个选项构成。
    1. perPage:分页量,此处代表每10条分一页。
    2. layout:生成页面时采用哪个模板。该变量接收String和Array两种值。String代表指定某个模板文件,Array代表优先使用哪个,当第一个不存在则沿用下一个。
    3. data:代表额外赋予到模板page对象变量上的其他属性。
    4. format:默认值为page/%d/,代表生成的URL为/archives/page/1/等形式。

三、开始编码

(1)编辑数据源

顾名思义即数据项来源,目前在Hexo中有2种数据源形式:md文件或者json数据文件

md文件可以通过tag关联或者category关联的形式把多个md文件内容聚合到模板页面上,例如下方示例:

1
2
3
4
5
// 获取指定Tag对象
let tag = site.tags.findOne({name: '自定义tag名称'})

// 通过tag.posts来遍历输出某个tag下的文章数据
tag.posts.map(...)

json数据文件是我用的比较多的一类,目前网站的几个拓展模块基本都是采用json文件来实现的数据维护更新。在source/_data目录下创建的.json数据文件都可以在模板中通过site.data.xxx来获取。

在这里我仍然沿用json数据文件的方式来编写“天工”模块,创建一个名为goods.json的数据文件。json内容如下:

1
2
3
4
5
6
7
8
9
10
[
{
"poster": "poster.jpg",
"title": "第一个天工产品",
"description": "这里是一行简短的产品特点介绍",
"score": "5", // 星星数
"price": "¥2024",
"link": "/article/waic-2024.html" // 点击后的访问链接
}
]

(2)创建模板

在模板文件夹下创建一个名为goods.ejs的模板(此处沿用了hexo默认的ejs模板,其他模板引擎需要调整为对应写法)。

下面是简版的模板代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 循环输出数据项 -->
<% page.posts.map(function(post, i){ %>
<p>封面:<%= post.poster %></p>
<p>标题:<%= post.title %></p>
<p>描述:<%= post.description %></p>
<p>评分:<%= post.score %></p>
<p>价格:<%= post.price %></p>
<p>访问链接:<%= post.link %></p>
<% }) %>

<!-- 仅在分页大于1页时展示分页数据 -->
<% if (page.total > 1){ %>
<%- paginator({
escape: false,
mid_size: 1
}) %>
<% } %>

(3)创建脚本

scripts目录下创建生成脚本,命名为generator-goods.js,内容如下:

1
2
3
4
5
6
7
8
9
10
const pagination = require("hexo-pagination");

hexo.extend.generator.register("goods", function (locals) {
return pagination("/goods", locals.data.goods, {
perPage: 10,
current: 1,
format: "page/%d/",
layout: "goods.ejs",
});
});

接着运行hexo server预览就能看到展示效果了。

四、功能优化

(1)菜单栏的选中效果失效

之前菜单栏的自定义页面可以通过is_page() && page.layout==="xxx"来匹配和实现当前菜单高亮效果,而通过hexo-pagination生成的页面is_page()结果为false,且page.layout为undefined

解决方法是调整generator-goods.js脚本内容,在pagination函数的data变量中补充layout属性信息,之后就可以通过page.layout === "goods"来匹配页面了。

1
2
3
4
5
6
7
8
9
10
11
12
13
const pagination = require("hexo-pagination");

hexo.extend.generator.register("goods", function (locals) {
return pagination("/goods", locals.data.goods, {
perPage: 10,
current: 1,
format: "page/%d/",
layout: "goods.ejs",
data: {
layout: 'goods' // 增加layout属性信息
},
});
});

(2)SEO信息优化

在md文件中,可以通过Front-matter来设置title和description,然后在模板中使用page.titlepage.description来获取对应信息,例如:

1
2
3
4
5
6
---
title: Hello World
description: This is a md file
---

内容...

解决方法还是调整generator-goods.js,在data中补充对应的属性信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const pagination = require("hexo-pagination");

hexo.extend.generator.register("goods", function (locals) {
return pagination("/goods", locals.data.goods, {
perPage: 10,
current: 1,
format: "page/%d/",
layout: "goods.ejs",
data: {
layout: 'goods',
title: 'Hello World', // 增加layout属性信息
description: 'This is a md file', // 增加layout属性信息
},
});
});

评论区