Next.js 14首屏加载时Suspense fallback在Safari失效(Chrome正常)
修复Safari中React Suspense不显示Fallback的问题
问题根源
直接将组件声明为async function并非React官方推荐的Suspense用法,这种写法的行为在不同浏览器中存在差异:Chrome会正确触发Suspense展示fallback,但Safari会等待异步组件完成数据获取后才渲染整个页面,导致Header和加载提示无法及时显示。
方案1:使用React 18+ use钩子配合Suspense兼容的数据获取
将数据逻辑与组件分离,通过抛出Promise让Suspense捕获,这是官方推荐的Suspense用法,能保证跨浏览器一致性:
import { Suspense, use } from "react"; // 单独的数据获取函数,抛出Promise供Suspense捕获 function fetchBlogPosts() { const dataPromise = fetch("https://httpbin.org/delay/5", { cache: "no-store" }) .then(res => res.json()); throw dataPromise; } function BlogPosts() { // 使用use钩子解析Promise const resjson = use(fetchBlogPosts()); return <div>{resjson.url}</div>; } function Page() { return ( <div> <h1>H E A D E R</h1> <div> <Suspense fallback={<div>fallback -> loading...</div>}> <BlogPosts /> </Suspense> </div> </div> ); } export default Page;
方案2:手动管理加载状态(兼容性更强)
如果需要兼容React 18以下版本,或者更精细控制加载逻辑,可以用useState和useEffect手动处理:
import { useState, useEffect } from "react"; function BlogPosts() { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { async function loadData() { try { const res = await fetch("https://httpbin.org/delay/5", { cache: "no-store" }); const resjson = await res.json(); setData(resjson); } finally { setIsLoading(false); } } loadData(); }, []); // 手动返回加载提示 if (isLoading) { return <div>fallback -> loading...</div>; } return <div>{data?.url}</div>; } function Page() { return ( <div> <h1>H E A D E R</h1> <div> <BlogPosts /> </div> </div> ); } export default Page;
方案对比
- 方案1更贴合React Suspense的设计理念,代码更简洁,但需要React 18及以上版本。
- 方案2兼容性更好,不依赖Suspense特性,适合需要兼容多版本或自定义加载逻辑的场景。
内容的提问来源于stack exchange,提问作者MMC
相关产品推荐
相关产品推荐

