Eleventy 在年月归档页面对文章进行分页实现方法

我在使用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">&laquo;</span>
					</a>
				</li>
			{% else %}
				<li class="page-item disabled">
					<a class="page-link" aria-label="Previous">
						<span aria-hidden="true">&laquo;</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">&raquo;</span>
					</a>
				</li>
			{% else %}
				<li class="page-item disabled">
					<a class="page-link" aria-label="Next">
						<span aria-hidden="true">&raquo;</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">&laquo;</span>
					</a>
				</li>
			{% else %}
				<li class="page-item disabled">
					<a class="page-link" aria-label="Previous">
						<span aria-hidden="true">&laquo;</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">&raquo;</span>
					</a>
				</li>
			{% else %}
				<li class="page-item disabled">
					<a class="page-link" aria-label="Next">
						<span aria-hidden="true">&raquo;</span>
					</a>
				</li>
			{% endif %}
			</ul>
		</nav>
	{% endif%}

通过这种方式就能实现生成所有归档日期下的文章分页了。效果可以参考:https://paulyu1988.github.io/

评论