Gatsby+Sanity静态构建异步数据获取失败问题求助
嘿,我完全懂你现在的困扰——开发环境里功能一切正常,Netlify构建完却一直卡在Loading状态,这根本原因是你当前用的是客户端侧异步数据获取,和Gatsby静态构建的逻辑不匹配。让我一步步帮你搞定这个问题:
为什么会出现这个问题?
你现在用useSWR发起的请求,是在组件挂载后(浏览器执行JS阶段)才触发的。开发环境中Gatsby的热加载机制会模拟客户端环境,所以能正常拿到数据;但Netlify构建静态站点时,只会执行构建阶段的代码,不会运行客户端的useEffect或useSWR这类异步逻辑,所以最终生成的静态页面里只有Loading文本,直到用户打开页面、浏览器执行JS后才会发起请求,这就导致了生产环境的加载异常。
正确思路:在Gatsby构建阶段预取所有数据
Gatsby的核心优势就是静态生成时提前拉取所有需要的数据,我们需要改用Gatsby的内置数据方案,把Sanity的数据在构建阶段同步到Gatsby的数据层,然后用GraphQL直接查询,彻底避免客户端异步请求。
第一步:确认Sanity数据源插件配置
首先确保你已经安装并配置了gatsby-source-sanity,它会把Sanity的数据同步到Gatsby的GraphQL数据层。你的gatsby-config.js配置大概是这样的:
module.exports = { plugins: [ { resolve: `gatsby-source-sanity`, options: { projectId: `你的Sanity项目ID`, dataset: `你的数据集名称`, watchMode: true, // 开发环境自动同步数据 overlayDrafts: true, }, }, ], }
第二步:根据组件类型选择数据获取方式
情况1:如果是页面组件——用pageQuery
页面组件可以直接导出GraphQL查询,Gatsby会在构建时自动执行这个查询,把数据作为props传给页面:
import React from "react" import { graphql } from "gatsby" import BlockCarouselBlog from "../components/BlockCarouselBlog" export default function BlogPage({ data }) { // 假设你的block数据来自页面配置或其他查询 const block = { itemAmount: 5 } return ( <div> <BlockCarouselBlog block={block} blogData={data.allSanityBlog.nodes} /> </div> ) } // 构建阶段执行的查询,提前拉取所有博客数据 export const pageQuery = graphql` query BlogPageQuery { allSanityBlog(sort: { fields: _createdAt, order: ASC }) { nodes { _id title excerpt // 这里列出你需要的所有博客字段 } } } `
情况2:如果是子组件——用useStaticQuery(静态场景)
如果你的子组件不需要动态参数,可以用useStaticQuery钩子直接在组件内执行构建阶段的查询:
import React from "react" import { useStaticQuery, graphql } from "gatsby" import BlockCarousel from "../blockCarousel/blockCarousel" const BlockCarouselBlog = ({ block }) => { const defaultLimit = 10 const limit = block.itemAmount ? block.itemAmount : defaultLimit // 构建阶段直接查询数据,无需客户端请求 const data = useStaticQuery(graphql` query BlogCarouselQuery { allSanityBlog(sort: { fields: _createdAt, order: ASC }, limit: 10) { nodes { _id title excerpt } } } `) // 根据需要的数量切片数据 const filteredBlogs = data.allSanityBlog.nodes.slice(0, limit) block.blocks = filteredBlogs return <BlockCarousel block={block} /> } export default BlockCarouselBlog
⚠️ 注意:useStaticQuery不支持动态参数(比如直接把limit作为变量传入查询),如果你的block.itemAmount是动态变化的,建议用页面组件查询全量数据,再通过props传给子组件的方案,灵活性更高。
最后调整组件逻辑
彻底移除原来的useSWR相关代码,改用上面的构建阶段查询方式。这样Netlify构建时会提前拉取所有需要的博客数据,生成的静态页面直接包含完整内容,不会再出现Loading状态。
内容的提问来源于stack exchange,提问作者Tim

