You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在next-sanity中按分类对博客文章进行分组排列

next-sanity 博客按分类分层展示实现方案

1 前置校验:确保Sanity Schema关联配置正确

先确认内容模型的引用关系配置无误,这是数据不串流的基础:

  • 分类(category)文档基础配置示例:
// schemas/category.js
export default {
  name: 'category',
  title: '文章分类',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: '分类名称',
      type: 'string',
      validation: Rule => Rule.required()
    },
    {
      name: 'slug',
      title: '分类标识',
      type: 'slug',
      options: { source: 'title' }
    },
    // 如需自定义分类排序,可新增该字段
    // { name: 'sortOrder', title: '排序值', type: 'number' }
  ]
}
  • 文章(post)文档中关联分类的字段配置示例:
// schemas/post.js 关联分类字段片段
{
  name: 'categories',
  title: '所属分类',
  type: 'array',
  of: [{ type: 'reference', to: [{ type: 'category' }] }],
  validation: Rule => Rule.required()
}

2 核心实现:用GROQ在查询层完成分类-文章绑定

不要先拉全量文章再在前端手动分组,这种写法很容易因为匹配逻辑错误出现跨分类混排。直接通过GROQ关联查询,在数据返回层就完成分类和对应文章的绑定,从根源避免串内容。
以Next.js App Router的服务端组件为例,页面代码如下:

// app/blog/page.js
import { client } from '@/sanity/lib/client'

// GROQ查询:按顺序拉取所有分类,同时携带每个分类下的关联文章
const CATEGORIES_WITH_POSTS_QUERY = `*[_type == "category"] | order(title asc) {
  _id,
  title,
  // 关联查询当前分类下的所有文章,按发布时间倒序
  "posts": *[_type == "post" && references(^._id)] | order(publishedAt desc) {
    _id,
    title,
    slug,
    excerpt,
    publishedAt,
    mainImage
  }
}`

export default async function BlogPage() {
  const categoriesWithPosts = await client.fetch(CATEGORIES_WITH_POSTS_QUERY)
  // 过滤掉没有关联文章的空分类
  const validCategories = categoriesWithPosts.filter(category => category.posts.length > 0)

  return (
    <div className="container mx-auto py-12 px-4">
      <h1 className="text-3xl font-bold mb-10">博客文章</h1>
      {/* 逐块渲染分类和对应文章 */}
      {validCategories.map(category => (
        <section key={category._id} className="mb-16">
          <h2 className="text-2xl font-semibold border-b border-gray-200 pb-2 mb-6">
            {category.title}
          </h2>
          <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
            {category.posts.map(post => (
              <article key={post._id} className="border rounded-lg p-4 hover:shadow-md transition-shadow">
                <h3 className="text-xl font-medium mb-2">
                  <a href={`/blog/${post.slug.current}`} className="hover:text-blue-600">
                    {post.title}
                  </a>
                </h3>
                <p className="text-gray-500 text-sm mb-3">
                  {new Date(post.publishedAt).toLocaleDateString('zh-CN')}
                </p>
                <p className="text-gray-700 line-clamp-3">{post.excerpt}</p>
              </article>
            ))}
          </div>
        </section>
      ))}
    </div>
  )
}

如果是Pages Router,把查询逻辑放到getStaticProps里拉取数据后传给页面组件即可,渲染逻辑完全一致。

3 常见问题排查

  • 若出现分类顺序不符合预期:给category文档增加sortOrder数字字段,手动设置每个分类的排序值,把GROQ查询里的排序规则改成| order(sortOrder asc)即可自定义展示顺序
  • 若仍出现混排:检查是否在渲染层错误遍历了全量文章数组,确保每个分类区块下只遍历当前分类返回的posts字段内容
  • 若某分类下文章缺失:去Sanity后台检查对应文章是否正确关联了目标分类,未关联reference的文章不会出现在查询结果中

内容的提问来源于stack exchange,提问作者Mohid Naeem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:09:22