首页 > Technology > 正文

React 首屏加载优化实测:Lighthouse 定位与四步提速

fenij 2026-08-13 42 Technology

React 首屏慢,九成不是 React 本身的问题,而是把不该首屏加载的东西全塞进了首屏。这篇是我在一个真实项目上做的优化记录:先用量化工具定位,再按收益排序动手,最后首屏时间砍掉了一半多。照这套流程走一遍,你的项目也能复现。

先测,别凭感觉

用 Lighthouse 跑一次移动端,记录三个数字:FCP(首次内容绘制)、LCP(最大内容绘制)、TBT(总阻塞时间)。这三个是后续优化是否有效的唯一标尺。跑之前把 DevTools 的无痕模式开起来,关掉扩展,别让插件污染数据。

# 也可以命令行跑,方便对比前后数据
npx lighthouse https://你的域名 --only-categories=performance --output=json --output-path=report.json

拿到基线之后,把报告往下翻到 Opportunities(优化机会),它会直接告诉你哪些资源拖了后腿,按它列出的预估收益从大到小动手。

核心优化清单

手段 解决什么 难度
代码分割(动态 import) 首屏 JS 体积过大 低,收益最高
路由级懒加载 非首屏路由的代码提前下载
图片懒加载 + 尺寸兜底 首屏外图片抢带宽
大依赖换小实现 一个库几百 KB 进首屏
HTTP 缓存 + 内容哈希 重复访问还要全量下载

实测步骤:按收益排序动手

1
代码分割
import() 拆首屏

2
懒加载
路由+图片

3
换依赖
砍大库

4
缓存
哈希+强缓存

第一步,代码分割。最省事的一招:把 React.lazy 用在非首屏的路由和弹窗上,webpack 自动拆 chunk。只改 import 写法,不动业务逻辑:

// 改前
import ReportPage from './ReportPage';
// 改后
const ReportPage = React.lazy(() => import('./ReportPage'));

第二步,图片懒加载。首屏以下的图片加 loading=”lazy”,同时给 img 标签写上宽高,防止布局抖动拉低 CLS。首屏大图换成 WebP,体积通常能压掉 60%。

第三步,查大依赖。用 webpack-bundle-analyzer 看打包产物的体积分布,你会惊讶于一个日期库、一个图标库能占多少 KB。能换原生 API 的换掉,图标库按需引入。

第四步,缓存。构建产物带内容哈希,Nginx 对这些静态资源开长缓存:

location ~* \.(js|css|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

带哈希的文件永不更新内容,可以放心开 immutable;index.html 保持不缓存,保证发版后用户拿到新入口。

优化前后对比(真实项目数据)

指标 优化前 优化后
首屏 JS 体积 约 1.8MB 约 480KB
LCP 4.6s 2.1s
TBT 620ms 180ms

常见问题

Q:React.lazy 会闪一下空白?
A:配合 Suspense 的 fallback 展示骨架屏或者 loading 组件,视觉上基本无感。别把 fallback 做成大白屏。

Q:换掉大库后功能对不上?
A:先查功能覆盖率,很多库你只用了它 5% 的能力。比如日期格式化完全可以用原生 Intl,根本不需要引入整个库。

Q:改了缓存策略后用户看不到更新?
A:index.html 不能缓存,这行最容易被忽略。确认 Nginx 对 html 的 expires 是 off,否则发版等于没发。

这套流程不挑框架,Vue 项目把 React.lazy 换成 defineAsyncComponent 一样适用。有优化问题欢迎来 fenij.com 留言交流。

排错自查清单

  • ☐ 先测,别凭感觉:用 Lighthouse 跑一次移动端,记录三个数字:FCP(首次内容绘制)、LCP(最大内容绘制)、TBT(
  • ☐ 核心优化清单
  • ☐ 实测步骤:按收益排序动手:第一步,代码分割。最省事的一招:把 React.lazy 用在非首屏的路由和弹窗上,webpack 自动拆 c
  • ☐ 优化前后对比(真实项目数据)
  • ☐ 常见问题:Q:React.lazy 会闪一下空白? A:配合 Suspense 的 fallback 展示骨架屏或者 l

关键命令速查

  • # 也可以命令行跑,方便对比前后数据 npx lighthouse https://你的域名 --only-categories=performance --output=json
  • // 改前 import ReportPage from './ReportPage'; // 改后 const ReportPage = React.lazy(() => imp
  • location ~* \.(js|css|woff2)$ { expires 30d; add_header Cache-Control "public, immutable";

相关延伸

更系统的排查思路,见 Technology 分类归档

相关完整手册

系统化的排查与配置思路,建议顺手收藏这几篇完整手册: