Archive Page
The archive page is a section of a blog that displays all posts in chronological order, typically presented in a timeline format, making it easy for readers to browse and search for historical articles.
Archive Page Template
The archive page template is a .archive.ts file located in the .kecare/ directory. The generator will call it after processing all articles to generate the archive page.
A Runnable Template
Create file .kecare/archive.archive.ts:
import { join } from 'node:path';
import type { ArchiveArticleData, KecareContext } from "kecare";
export const type = 'archive';
// 目标语言
const TARGET_LANGUAGE = 'zh-CN';
export async function generator(context: KecareContext, articles: ArchiveArticleData[]) {
const zhArticles: ArchiveArticleData[] = [];
// 筛选出目标语言的文章
for (const article of articles) {
if (article.lang === TARGET_LANGUAGE) {
zhArticles.push(article);
}
}
// 按日期排序
zhArticles.sort((a, b) => {
return new Date(b.date).getTime() - new Date(a.date).getTime();
});
return {
fsPath: join(context.projectPath, 'app', 'pages', 'archive.vue'),
template: `
<script setup lang="ts">
import ArchiveLanding from "~/components/archive-landing.vue";
const articles = ${JSON.stringify(zhArticles, null, 2)};
const totalArticles = ${totalArticles};
</script>
<template>
<ArchiveLanding
:articles="articles"
/>
</template>
`,
};
}
.archive.ts needs to return an object containing fsPath and template. The generator will use this information to create the archive page. Unlike landing pages, archive pages typically do not require pagination; all articles are displayed on a single page, grouped by year.
Page Components
We have invoked the <ArchiveLanding/> component in the template. Next, let's write the archive page component:
~/components/archive-landing.vue
<script lang="ts" setup>
type YearGroup = {
year: number;
articles: ArchiveArticleData[];
};
const props = defineProps<{
articles: ArchiveArticleData[];
totalArticles?: number;
}>();
// 按年份分组文章
const yearGroups = computed<YearGroup[]>(() => {
const groups: Map<number, ArchiveArticleData[]> = new Map();
for (const article of props.articles ?? []) {
const year = new Date(article.date).getFullYear();
if (!groups.has(year)) {
groups.set(year, []);
}
groups.get(year)!.push(article);
}
// 将 Map 转换为数组并按年份降序排列
const result: YearGroup[] = [];
const sortedYears = Array.from(groups.keys()).sort((a, b) => b - a);
for (const year of sortedYears) {
// 文章已在 generator 中按日期降序排序
result.push({ year, articles: groups.get(year) ?? [] });
}
return result;
});
// 收集所有标签
const allTags = computed<string[]>(() => {
const tagSet = new Set<string>();
for (const article of props.articles ?? []) {
for (const tag of article.tags ?? []) {
tagSet.add(tag);
}
}
return Array.from(tagSet);
});
// 格式化日期为 MM-DD
function formatDate(dateStr: string): string {
const date = new Date(dateStr);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${month}-${day}`;
}
</script>
<template>
<div class="archive-page">
<h1 class="page-title">文章归档</h1>
<div class="tags-section">
<h2>标签云</h2>
<div class="tags-list">
<span v-for="tag in allTags" :key="tag" class="tag-item">
{{ tag }}
</span>
</div>
</div>
<div class="timeline-section">
<h2>时间轴</h2>
<div v-for="group in yearGroups" :key="group.year" class="year-group">
<h3 class="year-title">{{ group.year }} 年</h3>
<div class="article-list">
<NuxtLink
v-for="article in group.articles"
:key="article.hash"
:to="article.urlPath"
class="article-item"
>
<span class="article-date">{{ formatDate(article.date) }}</span>
<span class="article-title">{{ article.title }}</span>
</NuxtLink>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>
ArchiveArticleData Type
The archive page component receives articles of type ArchiveArticleData, which is defined as follows:
type ArchiveArticleData = {
title: string; // 文章标题
lang: string; // 语言
hash: string; // 文章唯一标识
tags: string[]; // 标签列表
date: string; // 发布日期
fsPath: string; // 文件路径
urlPath: string; // 访问路径
}
Unlike landing pages, the data structure of archive pages is more streamlined, containing only the essential information needed for archive display, which helps reduce the size of the generated pages.