我在使用eleventy静态网站生成工具。我要在archive页面上列出所有文章的年月归档列表。点击某个年月后,会跳转到/archive/yyyyMM页面。这个页面上会显示yyyyMM日期范围内的文章列表,并且这个文章列表也要支持分页,每页显示10个文章。
使用AI和网上查询了一些做法,都是先group出来postsByYearMonth collection,然后依靠pagination循环生成归档页面。但是无法对每个yyyyMM归档年月日期里的文章再进行分页。leventy 本身不支持在一个模板里“嵌套两个 pagination”——也就是用外层 pagination 为不同年月生成页面,然后在那个页面里再让 Eleventy 的内层 pagination 作用于该年月的文章。这在社区里确实是一个常见的限制/痛点。
后来通过尝试想到一个解决方法。在group出来postsByYearMonth collection的时候,先对每个yyyyMM归档日期里的post分页好,然后在页面模板里通过pagination循环生成所有分页。下面是主要实现代码:
eleventy.config.js
eleventyConfig.addCollection("postsByYearMonth", collection => {
const all = collection.getAllSorted().filter(item => item.data.tags && item.data.tags.includes("posts"));
const groups = all.reduce((acc, post) => {
const date = post.date;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const key = `${year}-${month}`;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(post);
return acc;
}, {});
const pairs = Object.keys(groups)
.sort((a, b) => {
return b.localeCompare(a);
})
.map(key => {
return {
yearMonth: key,
posts: groups[key].sort((a, b) => b.date - a.date)
};
});
return pairs;
});
eleventyConfig.addCollection("postsByYearMonthPages", collection => {
const all = collection.getAllSorted().filter(item => item.data.tags && item.data.tags.includes("posts"));
const groups = all.reduce((acc, post) => {
const date = post.date;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const key = `${year}-${month}`;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(post);
return acc;
}, {});
const size = 2;
const result = [];
const pairs = Object.keys(groups).sort((a, b) => {
return b.localeCompare(a);
});
pairs.forEach(key => {
var posts = groups[key].sort((a, b) => b.date - a.date);
if (posts.length > size) {
const pageCount = posts.length % size ==0 ? posts.length/size : Math.floor(posts.length/size) + 1;
for(var i = 0; i < pageCount; i++) {
var pagePosts = posts.slice(i*size, (i+1)*size);
result.push({
yearMonth: key,
posts: pagePosts,
pageNumber: i + 1,
pageCount: pageCount,
code: key.replace(/-/g, "")
});
}
} else {
result.push({
yearMonth: key,
posts: posts,
pageNumber: 1,
pageCount: 1,
code: key.replace(/-/g, "")
});
}
});
return result;
});
// 你还可以加一些日期格式化的过滤器,比如把 “2025-08” 转成 “2025 年 08 月” 或 “Aug 2025” 等
eleventyConfig.addFilter("yearFromYearMonth", key => {
return key.split("-")[0];
});
eleventyConfig.addFilter("monthFromYearMonth", key => {
return key.split("-")[1];
});
- postsByYearMonth:这个collection用来循环输出归档日期。
- postsByYearMonthPages:这个collection里包含了分好页的归档日期和对应页面上的postlist。
archive.njk
archive页面分左右两栏,左侧列出了所有文章,使用Eleventy自带的pagination实现了分页。
右侧列出了年月归档日期,点击对应的年月日期会跳转到/archive/yyyyMM页面,查看该归档日期范围内的文章。
---js
const eleventyNavigation = {
key: "Archive",
order: 2
};
const pagination = {
data: 'collections.posts',
size: 2,
alias: 'postsOnPage'
}
const eleventyComputed = {
permalink : function (data) {
return data.pagination.pageNumber === 0
? "/archive/"
: `/archive/page/${data.pagination.pageNumber + 1}/`;
}
}
---
<div class="row">
<div class="col-lg-9">
{% set postslist = postsOnPage %}
{% include "postslist.njk" %}
{% if pagination.pages | length > 1 %}
<nav aria-label="Page navigation example">
<ul class="pagination">
{% if pagination.pageNumber > 0 %}
<li class="page-item">
<a class="page-link" href="{{ pagination.hrefs[pagination.pageNumber - 1] }}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{% for page in pagination.pages %}
{% set idx = loop.index0 %}
{% if idx == pagination.pageNumber %}
<li class="page-item active">
<a class="page-link" href="#" aria-current="page">{{ idx + 1 }}</a>
</li>
{% else %}
<li class="page-item"><a class="page-link" href="{{ pagination.hrefs[idx] }}">{{ idx + 1 }}</a></li>
{% endif %}
{% endfor %}
{% if pagination.pageNumber + 1 < pagination.pages | length %}
<li class="page-item">
<a class="page-link" href="{{ pagination.hrefs[pagination.pageNumber + 1] }}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif%}
</div>
<div class="col-lg-3">
<ul class="archive-year-month-list">
{% for item in collections.postsByYearMonth %}
{% set ym = item.yearMonth %}
{% set year = ym | yearFromYearMonth %}
{% set month = ym | monthFromYearMonth %}
<li>
<a href="/archive/{{ year }}{{ month }}/">
{{ year }} 年 {{ month }} 月
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
archive-monthly.njk
这个页面会列出某个归档年月日期范围里的文章,例如/archive/202509/。
通过pagination循环生成归档年月文章列表,因为postsByYearMonthPages里的数据已经是分页过的,所以会循环生成所有分页页面。
---js
const pagination = {
data: 'collections.postsByYearMonthPages',
size: 1,
alias: 'ymGroup'
}
const eleventyComputed = {
permalink : function (data) {
if (data.ymGroup.pageNumber == 1) {
return `/archive/${data.ymGroup.code}/`;
} else {
return `/archive/${data.ymGroup.code}/page/${data.ymGroup.pageNumber}/`;
}
}
}
---
<h1>{{ ymGroup.yearMonth | yearFromYearMonth }} 年 {{ ymGroup.yearMonth | monthFromYearMonth }} 月 归档</h1>
<ul class="archive-post-list">
{% for post in ymGroup.posts %}
<li>
<a href="{{ post.url }}">{{ post.data.title or post.url }}</a>
<time datetime="{{ post.date | htmlDateString }}">{{ post.date | readableDate }}</time>
</li>
{% endfor %}
</ul>
{% if ymGroup.pageCount > 1 %}
<nav aria-label="Page navigation example">
<ul class="pagination">
{% if ymGroup.pageNumber > 1 %}
<li class="page-item">
<a class="page-link" href="" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{% for i in range(1, ymGroup.pageCount + 1) %}
{% if i == ymGroup.pageNumber %}
<li class="page-item active">
<a class="page-link" href="#" aria-current="page">{{ i }}</a>
</li>
{% else %}
{% if(i == 1) %}
<li class="page-item"><a class="page-link" href="/archive/{{ ymGroup.code }}/">{{ i }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="/archive/{{ ymGroup.code }}/page/{{ i }}/">{{ i }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
{% if ymGroup.pageNumber < ymGroup.pageCount %}
<li class="page-item">
<a class="page-link" href="" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif%}
通过这种方式就能实现生成所有归档日期下的文章分页了。效果可以参考:https://paulyu1988.github.io/