归档页核心是按年份高效分组并倒序排列文章,需确保日期为iso格式、预提取年份、服务端分组须先排序且用annotate抽字段、thymeleaf用linkedhashmap保序。

归档页不是简单按年份堆文章,核心是让访问者一眼看清时间分布、快速定位目标内容。直接用 for 循环遍历所有文章再手动分组,容易在数据量变大时卡顿,且无法支持「某年某月有几篇」的聚合统计。
用 JavaScript 按年份分组文章列表(纯前端方案)
适用于静态博客或后端未提供归档结构 API 的场景。关键不是渲染,而是先做数据预处理:
- 必须确保每篇文章的日期字段是标准 ISO 格式(如
"2023-05-12T08:30:00Z"),否则new Date()解析可能出错 - 避免在循环中反复调用
getFullYear()—— 先用map()提取年份,再用reduce()分组,性能更稳 - 分组后记得对每个年份内的文章按时间倒序排列,否则最新文章会沉底
示例逻辑片段:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
const grouped = blogs.reduce((acc, blog) => {
const year = new Date(blog.date).getFullYear();
if (!acc[year]) acc[year] = [];
acc[year].push(blog);
return acc;
}, {});
<p>Object.keys(grouped).forEach(year => {
grouped[year].sort((a, b) => new Date(b.date) - new Date(a.date));
});
</p>
Django 模板里用 {% regroup %} 实现服务端归档
这是最轻量、最符合 Django 习惯的做法,但很多人卡在「为什么 regroup 不生效」——根本原因是被分组字段必须是**已排序的 QuerySet**,且分组字段需为字典属性(不能是方法调用)。
- 视图中必须显式调用
.order_by('-create_time'),且排序字段要和{% regroup %}的 by 字段一致(比如create_time__year) - 模板中不能写
{% regroup blogs by create_time.year %},因为.year是 Python 方法,Django 模板不支持;得提前在视图里把年份抽成字段:annotate(year=ExtractYear('create_time')) - 如果用了分页,
{% regroup %}必须在分页之后执行,否则只分当前页数据
Spring Boot + Thymeleaf 归档页的 key 配置点
Thymeleaf 的 th:each 和 Map 结构配合很自然,但容易忽略两件事:一是后端传入的归档 Map 必须是 LinkedHashMap(保证年份顺序),二是前端展示时年份标题的 HTML 层级容易混乱。
- Controller 中别用
HashMap构造archiveMap,否则年份可能乱序;改用new LinkedHashMap()并按年份降序插入 - Thymeleaf 模板里用
th:each="item : ${archiveMap}"时,item.key是年份(String),item.value是该年文章列表,别误写成item.year - 每个年份块外层建议包一层
<section></section>或带 id 的<div>,方便锚点跳转(比如 URL 带 <code>#2022)归档页真正的难点不在渲染,而在于数据组织方式是否与用户浏览路径匹配——比如用户想看「2022 年下半年」的文章,但你的归档只到年份层级,那就得补上月份聚合或加搜索过滤,而不是硬塞一个「全部展开」按钮。










