如何用GROQ查询获取90天前的Sanity博客文章?
问题分析与解决方案
你的代码存在两个核心问题导致页面无法渲染内容:GROQ查询的逻辑与语法错误,以及客户端组件使用async/await的兼容性问题。
1. 修复GROQ查询
原查询的逻辑完全颠倒:你需要获取发布时间超过90天的文章,应该筛选创建时间早于「当前时间减去90天」的内容,而非晚于。同时查询缺少必要括号,导致排序逻辑无法正确生效。
错误查询:
*[_type == 'post' && dateTime(_createdAt) >= dateTime(now()) - 60 * 60 * 24 * 90 | order(_createdAt desc)]
正确查询:
*[_type == 'post' && dateTime(_createdAt) <= dateTime(now()) - 60 * 60 * 24 * 90] | order(_createdAt desc)
- 将
>=改为<=,表示文章创建时间早于90天前的时间节点 - 给过滤条件添加括号,确保排序操作
| order(_createdAt desc)作用于整个过滤结果集
2. 解决客户端组件的async/await问题
你添加了"use client"指令,但Next.js的客户端组件不支持直接将默认导出的组件声明为async函数,这会导致组件无法正常渲染。你有两种解决方式:
方案一:移除"use client",使用服务器组件
服务器组件天然支持async/await数据获取,适合静态或动态渲染的页面:
// 移除顶部的 "use client" 指令 import React from 'react' import { client } from "../lib/sanity" import { Post } from "../lib/interface" import Link from "next/link"; import { PortableText } from '@portabletext/react'; import Image from "next/image"; async function getData() { const query = `*[_type == 'post' && dateTime(_createdAt) <= dateTime(now()) - 60 * 60 * 24 * 90] | order(_createdAt desc)`; const data = await client.fetch(query); console.log(data) return data; } export const revalidate = 60 export default async function movies() { const data = (await getData()) as Post[]; return ( <div className="xl:divide-y xl:divide-gray-200 xl:dark:divide-gray-700"> <header className="pt-6 xl:pb-6"> <div className="space-y-1 text-center"> <div> <h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-5xl md:leading-14"> Movies </h1> </div> </div> </header> <div className="divide-y divide-gray-200 pb-7 dark:divide-gray-700 xl:divide-y-0"> <div className="divide-y divide-gray-200 dark:divide-gray-700 xl:col-span-3 xl:row-span-2 xl:pb-0"> <div className="prose max-w-none pb-8 pt-10 dark:prose-invert prose-lg"> <ul> {data.map((post) => ( <li key={post._id} className="py-4"> <article className="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0"> <div> <p className="text-base font-medium leading-6 text-teal-500"> {new Date(post._createdAt).toISOString().split('T')[0]} </p> </div> <Link href={`/post/${post.slug.current}`} prefetch className="space-y-3 xl:col-span-3" > <div> <h3 className="text-2xl font-bold leading-8 tracking-tight text-gray-900 dark:text-gray-100"> {post.title} </h3> </div> <p className="prose max-w-none text-gray-500 dark:text-gray-400 line-clamp-2"> {post.overview} </p> </Link> </article> </li> ))} </ul> </div> </div> </div> </div> ) }
方案二:保留"use client",使用客户端数据获取库(如SWR)
如果必须使用客户端组件(比如需要交互逻辑),可以用SWR处理数据获取:
"use client" import React from 'react' import { client } from "../lib/sanity" import { Post } from "../lib/interface" import Link from "next/link"; import { PortableText } from '@portabletext/react'; import Image from "next/image"; import useSWR from 'swr' const fetcher = async () => { const query = `*[_type == 'post' && dateTime(_createdAt) <= dateTime(now()) - 60 * 60 * 24 * 90] | order(_createdAt desc)`; return client.fetch(query); } export default function movies() { const { data, error } = useSWR('old-posts', fetcher, { revalidateOnFocus: false, refreshInterval: 60000 // 对应原revalidate的60秒 }) if (error) return <div>加载失败</div> if (!data) return <div>加载中...</div> const posts = data as Post[] return ( <div className="xl:divide-y xl:divide-gray-200 xl:dark:divide-gray-700"> <header className="pt-6 xl:pb-6"> <div className="space-y-1 text-center"> <div> <h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-5xl md:leading-14"> Movies </h1> </div> </div> </header> <div className="divide-y divide-gray-200 pb-7 dark:divide-gray-700 xl:divide-y-0"> <div className="divide-y divide-gray-200 dark:divide-gray-700 xl:col-span-3 xl:row-span-2 xl:pb-0"> <div className="prose max-w-none pb-8 pt-10 dark:prose-invert prose-lg"> <ul> {posts.map((post) => ( <li key={post._id} className="py-4"> <article className="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0"> <div> <p className="text-base font-medium leading-6 text-teal-500"> {new Date(post._createdAt).toISOString().split('T')[0]} </p> </div> <Link href={`/post/${post.slug.current}`} prefetch className="space-y-3 xl:col-span-3" > <div> <h3 className="text-2xl font-bold leading-8 tracking-tight text-gray-900 dark:text-gray-100"> {post.title} </h3> </div> <p className="prose max-w-none text-gray-500 dark:text-gray-400 line-clamp-2"> {post.overview} </p> </Link> </article> </li> ))} </ul> </div> </div> </div> </div> ) }
3. 额外检查点
- 确认Sanity客户端
client配置正确,能正常连接到你的数据集 - 检查
Post接口是否包含_id、_createdAt、slug.current、title、overview这些字段,确保类型匹配 - 打开浏览器控制台,查看是否有报错信息,帮助定位其他潜在问题
内容的提问来源于stack exchange,提问作者blackheel
相关产品推荐
相关产品推荐

