很长一段时间里,我都以为性能优化是一件"灵感活":哪天心血来潮改一改,速度就快了。 直到把这套站点的首屏时间从 3.2 秒压到 0.8 秒之后,我才确认——它更像体检, 先测准,再诊断,最后按疗程用药。
先别急着优化
第一步其实是忍住不动手。我见过太多页面被"顺手优化"之后变得更慢:压缩了图片却引入了更大的字体库, 删掉了脚本却在首屏多塞了十几张图。任何一次改动都应该有明确的假设和可验证的结果, 否则它只是把不确定性搬了个地方。
没有测量就没有优化。凭感觉做的优化,通常只是把问题搬了个地方。
把指标测准
我最终固定下来看四个指标,它们的含义各不相同:
- FCP:首次内容绘制,用户感知"页面开始出来了"的时刻。
- LCP:最大内容绘制,通常是首屏那张主图或标题。
- CLS:布局偏移,衡量页面会不会"抖"。
- INP:交互响应,衡量点了之后多久有反馈。
采集方式很简单,一段不到二十行的代码就能上报到本地日志:
const report = {};
new PerformanceObserver((list) => {
for (const e of list.getEntries()) report[e.entryType] = e.startTime;
}).observe({ entryTypes: ['paint', 'largest-contentful-paint'] });
window.addEventListener('load', () => {
console.table(report);
});
定位真正的瓶颈
测完之后结果很意外:我以为的瓶颈是图片,实际上排在第一位的是第三方脚本的阻塞。
一个统计脚本在 <head> 里同步加载,把后面所有渲染任务都推迟了 900 毫秒。
这也是我后来坚持"全站资源本地化"的原因:外部依赖不仅带来隐私与合规问题, 还会把你页面的性能交给别人的服务器。
逐项动手
1. 让出首屏的渲染路径
把非关键脚本改为异步加载,样式内联进首屏,剩下的延后:
<link rel="stylesheet" href="css/style.css">
<script src="js/main.js" defer></script>
2. 图片按需与按尺寸
给所有图片加上明确的尺寸属性避免抖动,并按容器宽度提供合适尺寸:
<img src="images/cover-1.svg" width="1200" height="800"
alt="封面" loading="lazy" decoding="async">
3. 用 CSS 变量做主题,避免重排
深色模式切换如果靠替换整张样式表,会产生一次明显的重排与闪烁。改成语义化变量之后, 切换只是几十个变量值的变化,浏览器重绘代价极小。
把收益固化下来
优化最怕的是"下一次发版又变慢"。我的做法是给关键指标设置阈值, 超过阈值就在构建时给出警告,逼着自己在新功能上线前先回答"它让页面慢了多少"。
- 首屏 CSS 体积上限:14 KB
- 单张图片上限:180 KB
- 首屏请求数上限:12 个
写在最后
从 3.2 秒到 0.8 秒,没有任何一步是"黑魔法"。真正的区别只在于: 我终于开始相信数据,而不是相信手感。 如果你也在维护一个自己的小站,建议从今天开始,先给自己的页面做一次体检。