Next.js13中使用router.refresh()出现无样式内容闪烁问题
解决Next.js 13中router.refresh()导致的字体FOUC问题
一、核心原因
router.refresh()会触发全页面的重新渲染与数据验证,连带重新加载字体资源;而next/font/google默认配置若未做好缓存或预加载,就会导致字体重新下载时出现无样式内容闪烁(FOUC)。
二、具体解决方法
1. 全局单例化字体配置
务必在根layout.tsx中全局创建字体实例,避免在组件内重复生成,确保字体资源的请求稳定:
// app/layout.tsx import { Inter } from 'next/font/google'; // 全局单例字体实例,不要在子组件中重复定义 const inter = Inter({ subsets: ['latin'], variable: '--font-inter', display: 'swap', // 优先显示系统字体,待自定义字体加载后替换,减少闪烁 }); export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="zh-CN" className={inter.variable}> <body className={`font-sans ${inter.className}`}>{children}</body> </html> ); }
2. 配置字体资源长期缓存
在next.config.js中为字体资源设置强缓存策略,避免每次刷新都重新下载:
// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { headers: async () => { return [ { source: '/fonts/(.*)', // 匹配next/font生成的字体文件路径 headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', // 缓存1年且不可修改 }, ], }, ]; }, }; module.exports = nextConfig;
3. 改用局部数据更新替代全页刷新
如果业务场景允许,放弃router.refresh()的全页刷新,使用客户端状态管理工具(如SWR、React Query)做局部数据更新:
'use client'; import useSWR from 'swr'; const fetcher = (url) => fetch(url).then(res => res.json()); export function DataCard() { const { data, mutate } = useSWR('/api/user-data', fetcher); const handleUpdate = async () => { // 发送数据修改请求 await fetch('/api/update-user', { method: 'POST', body: JSON.stringify({ name: 'New Name' }) }); // 手动更新缓存,仅刷新当前组件数据 mutate(); }; return ( <div> <h3>{data?.name}</h3> <button onClick={handleUpdate}>修改名称</button> </div> ); }
三、Server Actions能否解决该问题?
Server Actions本身不会直接解决FOUC,但使用方式决定了是否会出现问题:
- 如果用Server Actions结合局部状态更新(比如useState、SWR的mutate),仅刷新需要变更的UI部分,不会触发全页渲染和字体重新加载,自然不会有FOUC;
- 但如果使用Server Actions后仍搭配router.refresh()触发全页刷新,还是会出现字体闪烁问题。
内容的提问来源于stack exchange,提问作者Yeats
相关产品推荐
相关产品推荐

