如何在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
相关产品推荐
相关产品推荐

