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才触发,这正常吗?是不是不应该在组件挂载时就加载全部数据?
解决方案可以从几个效率更高的角度入手:
第一,分页或者虚拟列表。500条数据一次渲染肯定是不合理的,用户也不可能同时看到这么多内容。你可以改成每次只加载前20-50条数据,后续通过滚动加载更多,或者用像react-window这样的库实现虚拟列表,这样只会渲染当前视口内的元素,性能会大幅提升。
第二,数据获取时机可以优化。你现在的代码是在组件挂载后才开始请求数据,这会让页面白屏时间变长。可以考虑把数据请求提前到服务端渲染(SSR)阶段,比如用Next.js做服务端数据预取,这样用户访问时HTML已经包含首屏数据,FMP会显著提升。
第三,懒加载不是拆分组件就够的,关键是控制资源加载优先级。你提到ProductCard用了Code Splitting,但这个对FMP帮助有限,因为瓶颈是数据量和渲染压力,而不是组件代码体积。建议给axios加个超时设置,并且确保接口响应头启用了gzip压缩。
第四,检查一下
ProductCard组件内部有没有不必要的复杂逻辑或重复计算。如果每个卡片都有一些昂贵的计算,可以用React.memo包裹避免重复渲染。给你一个优化后的代码示例:
总结一下:分页或者虚拟列表是最直接有效的优化手段,同时尽量让数据请求更早触发。别一股脑全塞给前端渲染,效率太低了。