从 3.2s 到 0.8s:一次真实的首屏加载优化复盘
这个项目本身没有任何"技术亮点"——一个典型的中后台系统,Vue 3 + Vite,二十来个路由,最重的页面挂了三张表格和两个图表。上线半年后,运营反馈"打开有点慢",于是有了这次优化。
先说结论:首屏从 3.2s 降到 0.8s,主要靠三件事——把首屏不需要的东西全部推迟、把入口包拆干净、以及把一张 1.2MB 的图换掉。
一、先量,不要先猜
动手之前先建立基线。我用 lighthouse 跑了三次取中位数,同时打开 Performance 面板看主线程占用:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| FCP | 1.9s | 0.6s |
| LCP | 3.2s | 0.8s |
| 主线程阻塞 | 1.4s | 0.2s |
| 首屏 JS(gzip) | 412 KB | 96 KB |
不要在没有基线的情况下做优化。你会很努力地改一堆东西,然后无法证明它们有用。
二、把入口包拆干净
原项目所有路由都用同步 import,构建出来只有一个 1.1MB 的入口 chunk。改成路由级懒加载之后,首屏只需解析首页相关的部分:
// 改之前:所有页面都在入口包里
import Dashboard from '@/views/Dashboard.vue'
import ReportList from '@/views/ReportList.vue'
// 改之后:只在真正进入路由时加载
{
path: '/report',
component: () => import('@/views/ReportList.vue'),
meta: { prefetch: false }
}
这一步单独就砍掉了约 260KB 的首屏 JS。关键在于:懒加载不是"加上就有效",你得同时把 prefetch 关掉,否则浏览器会在空闲时把后面的包全拉下来,网络好的时候看不出差别,弱网下反而更糟。
顺手解决的一个坑
仓库里有个 utils/ 目录被所有页面引用,里面混着日期格式化和一个 300 行的图表配置。图表配置被懒加载的组件引走之后,日期工具却因为挂在一个 barrel 文件上,仍然被打进了入口包——这种隐式依赖只能靠 rollup-plugin-visualizer 的产物图看出来。
三、图片是最大的意外
首页那个"品牌插画"是一张 1.2MB 的 PNG,用在一个 480×320 的位置上。换成 WebP 之后是 68KB,视觉上几乎无法分辨。
不要无脑给所有图加 loading="lazy"。首屏可见区域的 LCP 图片如果被懒加载,LCP 反而会显著变差——它要等布局完成才开始请求。
四、哪些"最佳实践"其实没帮上忙
- HTTP/2 服务端推送:配置成本高,浏览器已普遍弃用该特性,收益接近零。
- 全量 gzip 换成 brotli:体积确实小了 12%,但因为前面已经砍到 96KB,绝对收益只有 11KB,感知不到。
- 手动拆 vendor chunk:在 Vite 默认策略下反而多出两个额外请求,实测 TTFB 没有变化。
优化的第一性原理不是"做更多优化动作",而是减少首屏必须完成的工作量。所有动作都应该服务于这个目标,否则就只是在搬家。
五、可以复用的检查清单
下次遇到类似问题,我会按这个顺序走:
- 建立可对比的基线(三项指标 + 产物报告)
- 看入口包里到底装了什么,逐项判断是否属于首屏必要
- 检查首屏可见图片的格式、尺寸与压缩质量
- 确认没有把首屏需要的资源标成懒加载
- 最后才考虑缓存策略、CDN 和协议层
还有一点想说:这次优化过程中最有价值的产出不是那 2.4 秒,而是那份"哪些做法无效"的清单。它让我在下个项目里少走了不少弯路。
评论
还没有评论,坐个沙发?