React页面FMP分数低,为什么首次内容渲染这么慢?
我正在优化一个产品列表页面,用React+Axios加载数据后渲染列表,Lighthouse测FMP有4秒多,但代码已经用懒加载了。代码里用useEffect获取数据,但感觉首次渲染卡在某个环节…
function ProductList() {
const [products, setProducts] = useState([]);
useEffect(() => {
(async () => {
const res = await axios('/api/products');
setProducts(res.data); // 这里返回了500条数据
})();
}, []);
return (
<div>
{products.map(product => (
<ProductCard key={product.id} data={product} />
))}
</div>
);
}
我尝试过把ProductCard拆成Code Splitting,但FMP没改善。用Chrome检查发现,页面在渲染完500条数据后FMP才触发,这正常吗?是不是不应该在组件挂载时就加载全部数据?
几个能直接改的方案:
1. 先显示骨架屏或占位内容
让页面在数据回来之前就有东西可渲染:
2. 分页或懒加载渲染,不要一次出500条
这个最关键,500条一起渲染谁也扛不住:
3. 用虚拟列表(react-window)
如果一定要展示500条,虚拟列表是必选的,只渲染可视区域内的元素:
核心就两点:让页面在数据回来前有东西渲染(骨架屏),别一次搞500个DOM节点出来。做完这些FMP应该能降到1秒以内。
解决方案可以从几个效率更高的角度入手:
第一,分页或者虚拟列表。500条数据一次渲染肯定是不合理的,用户也不可能同时看到这么多内容。你可以改成每次只加载前20-50条数据,后续通过滚动加载更多,或者用像react-window这样的库实现虚拟列表,这样只会渲染当前视口内的元素,性能会大幅提升。
第二,数据获取时机可以优化。你现在的代码是在组件挂载后才开始请求数据,这会让页面白屏时间变长。可以考虑把数据请求提前到服务端渲染(SSR)阶段,比如用Next.js做服务端数据预取,这样用户访问时HTML已经包含首屏数据,FMP会显著提升。
第三,懒加载不是拆分组件就够的,关键是控制资源加载优先级。你提到ProductCard用了Code Splitting,但这个对FMP帮助有限,因为瓶颈是数据量和渲染压力,而不是组件代码体积。建议给axios加个超时设置,并且确保接口响应头启用了gzip压缩。
第四,检查一下
ProductCard组件内部有没有不必要的复杂逻辑或重复计算。如果每个卡片都有一些昂贵的计算,可以用React.memo包裹避免重复渲染。给你一个优化后的代码示例:
总结一下:分页或者虚拟列表是最直接有效的优化手段,同时尽量让数据请求更早触发。别一股脑全塞给前端渲染,效率太低了。