Astro 静态站如何做分类与标签

Astro 的分类与标签页怎么搭,才能可扩展、能排名、还能避开薄页陷阱——基于 Content Collections 的实战做法。

分类与标签页可以是站内链接最强的节点,也可以是最大的薄页负担。技术做法一样,关键看你在上面放了多少真内容。

问题背景

Astro 用 getStaticPaths + content collection 做分类/标签页很简单,技术上一下午搞定。难的是策略——确保这些页面有足够内容能排名。一个只有 4 篇文章列表、没介绍的分类页,就是个待爆的 soft 404。

判断标准

  • 已有 30+ 篇,需要浏览页帮读者整理。
  • 希望 hub 页面能排短尾词。
  • 预期读者会在 Google 入口页之外继续探索。
  • 每个 hub 能写 200-300 字的介绍。

快速结论

分类页当成完整内容页来做——有介绍、有列表、有 FAQ。标签页做精简,长期还薄就 noindex。

实操步骤

  1. src/content/config.ts 里给 articles 加 categorytags
import { defineCollection, z } from 'astro:content';

const articles = defineCollection({
  type: 'content',
  schema: z.object({
    title: z.string(),
    description: z.string().min(120).max(160),
    category: z.enum(['indie-dev', 'ai-tools', 'prompt-library']),
    tags: z.array(z.string()).default([]),
    publishedAt: z.date(),
    draft: z.boolean().default(false),
  }),
});

const categories = defineCollection({
  type: 'data',
  schema: z.object({
    slug: z.string(),
    title: z.string(),
    intro: z.string().min(200),    // 强制写真介绍,不许一行了事
    faq: z.array(z.object({ q: z.string(), a: z.string() })).default([]),
  }),
});

export const collections = { articles, categories };
  1. getStaticPathssrc/pages/[category]/index.astro 生成分类索引页:
---
import { getCollection, getEntry } from 'astro:content';
import { paginate } from 'astro:paginate';

export async function getStaticPaths({ paginate }) {
  const allArticles = await getCollection('articles', e => !e.data.draft);
  const categories = await getCollection('categories');

  return categories.flatMap(cat => {
    const list = allArticles.filter(a => a.data.category === cat.data.slug);
    return paginate(list, {
      params: { category: cat.data.slug },
      props: { meta: cat.data },
      pageSize: 24,
    });
  });
}

const { page, meta } = Astro.props;
---
<html>
  <head>
    <title>{meta.title}</title>
    <link rel="canonical" href={`https://yourdomain.com/${meta.slug}/`} />
  </head>
  <body>
    <h1>{meta.title}</h1>
    <p>{meta.intro}</p>

    <ul>
      {page.data.map(a => (
        <li><a href={`/articles/${a.slug}/`}>{a.data.title}</a></li>
      ))}
    </ul>

    {page.url.prev && <a href={page.url.prev}>上一页</a>}
    {page.url.next && <a href={page.url.next}>下一页</a>}
  </body>
</html>
  1. 每个分类写 200-300 字真介绍,存成 src/content/categories/ 下的 JSON / YAML:
# src/content/categories/indie-dev.yaml
slug: indie-dev
title: Indie Dev — 一个人做、上线、做收入
intro: >
  写给独立开发者的实战文章,覆盖大部分教程跳过的部分:
  域名决策、托管平台权衡、App Store 提审的坑、变现、
  以及对月访问量在 1 万以下的站点真正有效的 SEO 工作。…
faq:
  - q: 第一个站要做双语吗
    a: 只在你能长期维护两种语言的前提下。
  1. 标签页放 src/pages/tags/[tag].astro,做精简;按数量动态加 robots meta:
---
const articlesWithTag = (await getCollection('articles'))
  .filter(a => a.data.tags.includes(tag));
const shouldNoindex = articlesWithTag.length < 5;
---
<head>
  {shouldNoindex && <meta name="robots" content="noindex,follow" />}
  <link rel="canonical" href={`https://yourdomain.com/tags/${tag}/`} />
</head>
  1. 渲染顺序:介绍 → 文章列表 → FAQ → 相关分类。每一块都加 Google 能看到的份量。

  2. 构建后数生成页面,抓”标签爆炸”问题:

npm run build
find dist/tags -name 'index.html' | wc -l
# 超过 200 就是标签爆炸
  1. 决定 noindex 策略:分类保持可收录;标签页文章不到 5 篇就 noindex。

容易踩的坑

  • 分类页只放列表没介绍——Google 直接当薄页。
  • 从每篇文章的关键词自动生成标签,最后几百个空标签页。
  • 忘做分页,一个分类一页 200 篇,CWV 跪。
  • 同一文章同时挂多个分类却没处理 canonical,出现重复。
  • 把分类名硬编码在 URL 里,改名就死链。

这篇适合谁

30+ 篇规模、想做分类页 SEO 和读者留存的 Astro 内容站。

这篇不适合谁

一个时间线列表就够的小博客。

FAQ

  • 标签页要 noindex 吗: 2026 年默认建议 noindex,除非你给标签页写真内容。空标签页纯 SEO 负担。
  • 分类页能排短尾词吗: 能,但必须有真正的介绍内容和从文章反链到分类的强内链。
  • 长分类怎么分页: 用 Astro 的 paginate API,每页 20-30 条,加 prev/next 和自指 canonical。
  • 标签和分类区别是什么: 分类稳定、数量少,定义结构;标签流动、数量多,只有读者需要横切时才用。

相关阅读

标签: #独立开发 #Astro #Content Collections #SEO #Pillar / Cluster