FCP太慢了,首屏加载白屏好几秒怎么办?

码农爱香 阅读 23

我们首页上线后 Lighthouse 报 FCP 超过 4 秒,用户一进来就是白屏,体验很差。已经做了图片懒加载和代码分割,但首屏关键资源还是加载太慢。

关键 CSS 是内联的,但字体文件和首屏数据请求好像拖慢了渲染。比如这个 Google Fonts 的引用:

<link rel="preconnect" href="https://fonts.googleapis.com" rel="external nofollow" >

还有主接口在 useEffect 里调用,是不是应该提前?试过把字体换成本地加载,但体积又变大了。有没有更有效的 FCP 优化方案?

我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
西门利利
凌晨三点看到这问题,太真实了。你的 FCP 卡在 4 秒主要是两个坑:字体阻塞渲染和接口请求时机不对。

字体这块,preconnect 只是个开始,你得加 preload 和 font-display: swap,让字体加载不阻塞文字渲染:





CSS 里加上这个:

@font-face {
font-family: 'YourFont';
font-display: swap;
src: url('https://fonts.gstatic.com/s/yourfont.woff2') format('woff2');
}


数据请求才是大头,useEffect 里调接口等于等 JS 加载完才开始请求,黄花菜都凉了。两个路子:要么上 SSR/SSG 提前渲染好数据,要么在 HTML 里内联关键数据,或者至少把请求放到 script 标签里并行加载:



组件里直接用:

const data = await window.__INITIAL_DATA__;


字体本地化体积大的话,用 subset 只保留需要的字符,Google Fonts 可以加 &text= 参数裁剪。
点赞
2026-03-02 12:06
Zz宝玲
Zz宝玲 Lv1
4秒的FCP确实太夸张了,用户早跑了。看了你的描述,主要瓶颈应该就在字体和接口请求这两个地方。

先说字体,你只做了preconnect还不够,得加上preload才能真正提前加载。把关键字体文件加上preload,同时一定要设置font-display: swap,这样字体还没加载完先用系统字体渲染,不会白屏。Google Fonts的话可以在URL里加display=swap参数,或者本地化之后在@font-face里设置。本地化体积大的问题,用woff2格式,再配合subset只保留需要的字符,体积能压到几KB。

接口请求放useEffect里确实是问题所在。React组件渲染完了才开始请求,等于白白浪费了一轮。几个方案可以考虑:

第一,把关键数据提到组件外面,用Promise提前发起请求,组件渲染时直接await或者用Suspense。第二,如果用Next.js或类似的框架,直接走SSR或SSG,首屏数据服务端渲染好再返回。第三,用preload标签预加载接口数据,返回的HTML里直接注入初始状态。

给你一个比较实用的做法,在HTML里直接内联关键数据:



然后在你的React入口处:

const initialData = window.__INITIAL_DATA__ || null;

function App() {
const [data, setData] = useState(initialData);

useEffect(() => {
if (!data) {
fetch('/api/critical-data').then(res => res.json()).then(setData);
}
}, []);

// ...
}


服务端渲染HTML的时候把数据直接注入到window.__INITIAL_DATA__里,这样首屏渲染完全不需要等接口。

注意安全:preload的数据接口不要包含用户敏感信息,因为preload请求是公开的。另外如果用内联数据注入,要对数据做XSS转义,防止恶意数据注入攻击。CSP头也要配好,限制脚本来源。

还有个细节,你的preconnect要配完整,Google Fonts需要同时preconnect域名和gstatic:




这些优化加起来,FCP压到1.5秒以内应该没问题。再慢就得看看是不是bundle体积太大或者服务端响应慢了。
点赞
2026-02-28 23:04