Next.js对接Sanity拉取博客时PortableText报未知块类型错误
报错原因
触发这个报错有三个核心原因:
- 你使用的新版
@portabletext/react组件API和你写的传参方式不兼容:v2版本之后,原来接收富文本内容的content属性改名为value,接收自定义渲染规则的serializers属性改名为components,你传入的projectId、dataset属性组件本身根本不识别,整套自定义配置完全没有生效。 - 自定义渲染规则的结构不符合新版要求:旧版支持扁平传入h1、li这类渲染函数,新版要求按类型分类配置,块级标题、段落放在
block下,列表项放在listItem下,自定义Schema块放在types下,文本标记放在marks下,结构不对时组件找不到对应块的渲染方法,就会抛出块类型为undefined的错误。 - 数据拉取没有做兜底校验:GROQ查询直接拉取全量
blog类型数据,没有过滤content字段为空的条目,也没有判断返回数组是否为空,直接取blogs[0].content很容易拿到undefined值,触发渲染错误。如果Sanity后台的富文本里存了没有_type属性的异常块、或者配置了自定义Schema块(比如图片、代码块、引用卡片)但没写对应渲染规则,也会触发同类报错。
修复步骤
- 先确认安装的PortableText依赖是官方最新的
@portabletext/react,卸载旧的@sanity/block-content-to-react避免版本冲突。 - 调整组件传参和自定义渲染配置的结构,匹配新版API要求。
- 给GROQ查询加字段投影和过滤,给渲染逻辑加空值兜底,避免异常数据触发报错。
- 检查Sanity博客Schema中content字段用到的所有自定义块类型,在配置的
types节点下补全对应的渲染组件。
修复后的参考代码
// 页面组件部分 import { PortableText } from '@portabletext/react' // 按新版要求定义富文本渲染组件 const portableTextComponents = { block: { // 自定义h1渲染,其余块级标签如果不需要特殊样式可以不写,走默认渲染 h1: ({ children }) => <h1 style={{ color: "red" }}>{children}</h1>, }, listItem: { // 自定义无序列表、有序列表的列表项样式 bullet: ({ children }) => <li className="special-list-item">{children}</li>, number: ({ children }) => <li className="special-list-item">{children}</li>, }, types: { // 这里补全所有自定义块的渲染逻辑,比如Sanity自带的图片类型: // image: ({ value }) => <img src={/* 自行拼接Sanity图片地址 */} alt={value.alt || ''} /> // 没有自定义块时保留空对象即可,避免类型查找报错 } } export default function BlogPage({ blogs = [] }) { return ( <div className="blog-content"> {/* 加空值兜底,避免访问不存在的属性报错 */} {blogs?.[0]?.content ? ( <PortableText value={blogs[0].content} components={portableTextComponents} /> ) : ( <p>暂无博客内容</p> )} </div> ) } // getServerSideProps部分 export async function getServerSideProps() { const client = createClient({ projectId: 'oeqragbg', dataset: 'production', useCdn: false, apiVersion: '2024-01-01' // 必须指定API版本,避免返回数据格式异常 }); // GROQ查询加过滤和字段投影,只拉取需要的有效数据 const query = `*[_type == "blog" && defined(content)]{ _id, title, content }`; const blogs = await client.fetch(query); return { props: { blogs: blogs || [] } } }
内容的提问来源于stack exchange,提问作者Sushant Bansode
相关产品推荐
相关产品推荐

