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.

文章作者:
文章链接:kecare.me/articles/93b7f9a7
版权声明: 博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源