前端性能优化实战:从LCP 4.5秒降到1.8秒的经历

阿乐
阿乐 管理员 黑卡会员
发布于 2026-09-06 17:59 ·2 浏览 ·0 回复

事情发生在上个月。公司的一个核心业务后台系统,用户反馈“打开特别慢”,尤其是首屏基本白屏时间很长。我拉了一下性能数据,LCP(Largest Contentful Paint,最大内容绘制)平均在 4.5 秒左右——这个数字在内部系统里还能忍,但放到面向客户的场景下,几乎等于劝退。

说实话,起初我以为是网络问题,或者服务器带宽不够。但看了 Network 面板后,发现事情没那么简单。这是个典型的“什么都往首屏塞”的项目:一个 HTML 响应 200 多 KB,JS 打包后主 chunk 超过 1.2MB,图片没做任何懒加载,字体文件更是直接阻塞渲染。

于是,我花了一周时间做了几件事,最终把 LCP 降到了 1.8 秒以内。整个过程算不上高深,但每一步都是实打实的收益,在这里分享一下。

不是玄学,是能查出来的

优化前,第一步永远是量化。我用 Lighthouse 跑了三轮,记录下关键指标:LCP 4.5s,FCP 2.8s,TTI 6.2s,CLS 0.12。诊断建议里,“减少未使用的 JavaScript”“预加载关键请求”“适当调整图片大小”这几项赫然在列。

但 Lighthouse 只能给方向,不能给细节。于是我打开 Performance 面板录制了一次加载过程,仔细看了主线程的时间线。结果很明显:HTML 解析很快,但脚本执行阶段被一个巨大的 JS 文件卡住了。一个组件库里所有的弹窗、表格、日期选择器全部被打进了主 bundle,连没用到的图表库也被 import 了。

进一步排查发现,首屏里有一个轮播图组件,图片不是懒加载的,浏览器在下载 JS 的同时还要去拉三张 2MB 的背景图。另外,字体文件是自托管的 woff2,但被 CSS 里的 `@font-face` 在渲染前就阻塞了。这些问题叠加在一起,LCP 自然就崩了。

从源头砍掉不需要的请求

第一步,先解决资源体积问题。我把主 bundle 用 webpack 的 `splitChunks` 做拆包,第三方库单独抽出来走 CDN。更重要的是,项目之前的很多模块都是同步 `import`,我改成了路由级别的动态 `import()`,让首屏只加载真正需要的部分。

这个过程不复杂,但需要仔细检查每个 import 路径。我删掉了两三个被“顺手”引入但从未使用的工具库,把一些大图从 PNG 转成了 WebP。主 bundle 从 1.2MB 缩到了 280KB,减少了近 80%。

接着是图片懒加载。给所有首屏以下的图片加了 `loading="lazy"`,首屏的轮播图改成只加载第一张,后续的图片在滑动时再异步加载。同时给第一张图加了 `fetchpriority="high"`,让浏览器优先安排下载。

字体这块,我调整了 CSS,把 `font-display` 从默认的 `block` 改成了 `swap`,并单独把字体文件加上了 `preload`。这样文字先以系统字体渲染,等自定义字体下载完再替换,避免了白屏等待。

代码执行和数据,是两个大头

资源体积降下来之后,LCP 有了明显改善,大概降到了 2.8 秒左右。但离 1.8 秒还有距离。我看了一下 Performance 面板,发现 JS 执行的时间占用还是太长,页面已经能画出来了,但主线程一直忙于执行脚本,导致图片等内容的绘制被卡住。

这次定位到了一个问题:某个全局混入的工具函数,在每次路由切换时都会执行一次深度遍历,把整个 store 里的数据全部序列化一遍。而首屏时 store 里的数据量有 1 万多条,这个遍历直接吃掉了 800ms。

解决方式很简单,给这个逻辑加了缓存和节流,同时把数据请求从页面挂载前改成了挂载后,用骨架屏占位。首屏的 TTI 从 6.2 秒降到了 3 秒左右,LCP 最终稳定在 1.8 秒以下。

细节的优化也不容忽视

顺便处理了几个小问题:

- DNS 预解析:给所有跨域的 CDN 和 API 地址加了 `<link rel="dns-prefetch">`;
- 预连接:对主要的资源来源加了 `preconnect`;
- Gzip 改 Brotli:服务端开启了 Brotli 压缩,文本资源的体积又小了 20%;
- 缓存策略:静态资源加上了 immutable 缓存,二次访问基本秒开。

这些改动单独拿出来每一项收益都很小,但综合起来,积少成多。

性能优化不是一次性的事

做完这一切之后,我把 Lighthouse 跑了一遍,性能评分从 47 分提到了 92 分。LCP 从 4.5 秒降到 1.8 秒,FCP 从 2.8 秒降到 1.2 秒。但说实话,更让人安心的是,这些改动不是临时的补丁,而是把构建配置和页面加载规则都固定了下来。

现在团队的新代码都要求走代码审查,主要看有没有新加大体积的依赖、图片是否设置了宽高和懒加载、路由是否需要动态引入。

性能优化这件事,没有一劳永逸。只要你不在乎,两三个月之后,LCP 又会不知不觉涨回去。但只要你建立了检查机制,它就能一直保持在一个健康的基线之上。这次的 4.5 到 1.8,不是终点,只是一个开始。

全部回复 0

还没有回复,来抢沙发~