优化UI性能
1)算什么"快速"
TTFB(第一个字节之前的时间)是服务器/CDN的快速响应。
LCP(Largest Contentful Paint)-"主要"内容很快出现。
INP (Interaction to Next Paint)-在交互时响应。
CLS (Cumulative Layout Shift)-不存在"抖动"接口。
TTI (Time to Interactive)-当一切都已经响应时。
建议的地标:LCP ≤ 2。5 s,INP ≤ 200毫秒,CLS ≤ 0。1(对于第75个实际用户)。
2)过程: 衡量→发现瓶颈→提交预算
1.测量:RUM(实际用户,按国家/网络/Devaysom分级)+合成(灯塔/观察者)。
2.查找:Performance Profiler(长任务>50 ms,布局擦除,多余渲染)。
3.记录:预算(JS/CSS/字体重量,LCP/INP)和CI中的"红线"。
3)网络和资源下载
3.1个HTTP和优先级
启用HTTP/2/3,Brotli压缩。
"preconnect"到关键域;"dns-prefetch"到次要域。
关键资源的"preload"(英雄图像,主字体)。
支持的"fetchpriority="high/low"和"priority"线索。
3.2缓存
带有文件哈希的静态:"Cache-Control:public,max-age=31536000,immutable"。
HTML是通过CDN的简短TTL+stale-while-revalidate。
用于离线/重复访问的ETag/Last-Modified和Service Worker。
4)代码: 小,后来,"更平等"
4.1组装
Tree-shaking, minify (в т.ч. dead-code-elim).
在路由/小部件上进行代码分割,动态导入。
避免在基本乐队(瞬间→ Intl/Day中出现"全局"重包。js).
4.2 HTML渲染和交付
SSR/ISR/流媒体:首先提供框架和主内容。
Partial/Islands hydration:仅对交互式区域进行氢化。
Defer都是非关键的:"<script type="module" defer>"。
4.3反应细节(如果使用React)
`React.懒惰的小部件的lazy'+ 'Suspense'。
"startTransition"和"useDeferredValue"用于重型过滤器/搜索。
RSC(服务器组件)-服务器上的计算小于客户端上的JS。
堆栈中的选择器(zustand/redux):将组件签入片段,而非整个堆栈。
5)渲染和状态: 其中"刹车"
5.1 Reenders隔离
粉碎大组件,模因("memo","useMemo","useCallback")。
列表密钥是稳定的;不要在没有必要的情况下创建新的功能/设施。
避免频繁更改的数据的"全局"上下文,使用选择器或事件总线。
5.2虚拟化和大列表
工作表/表格→虚拟化(渲染窗口)。
分离/不定式滚动带背压盘(不要一次装载10万行)。
Wewport外的重小部件延迟初始化。
5.3 Layout & paint
content-visibility: auto;对于隐藏部分(浏览器不会渲染不可见部分)。
contain和"contain-intrinsic-size"可预测尺寸。
避免频繁读取布局输入记录(布局擦除);分组测量。
使用剂量(否则多余的内存/图层)。
6)图像和图形
格式:AVIF/WebP(PNG/JPEG上的后退)。
响应方法:"srcset"+"sizes",基于视网膜的density。
非英雄图像的'loading='lazy';priority/preload-仅适用于LCP候选人。
固定尺寸的播放器→没有CLS跳跃。
Canvas/图表:用于计算的offscreen-canvas和Web Worker;重新绘制。
7)字体和文字
一到两个可变字体代替许多字体。
"font-display: swap'/' optional", preload for main driving。
"size-adjust"以减少字体替换时的"跳跃"。
具有相似度量的本地fallback字体。
8) CSS和动画
关键的CSS入口(<14-20 kB),其余的是延迟。
删除未使用的样式(Purge/CSSTree)。
如果可能的话,在transform/opacity上进行动画;尊重"prefers-reduced-motion"。
避免深层级联和爆炸性选择器。
9) Web Workers,线程和繁重任务
所有CPU重都在Worker中(解析、排序、聚合、ML)。
流媒体API("ReadableStream",带有线程的"fetch")用于长响应。
通过"requestIdleCallback"/microtaski将任务分解到袜子上,以保持响应能力。
10)布局稳定性(CLS)
在LCP元素下方保留一个位置(图像/小部件)。
不要在没有固定尺寸的情况下插入横幅/磁带。
不对称的tultips/tosts-不移动内容;使用图层/门户。
11)嗅探示例
关键字体和LCP影像
html
<link rel="preload" href="/fonts/Inter. var. woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" as="image" href="/hero. avif" imagesrcset="/hero. avif 1x, /hero@2x. avif 2x" fetchpriority="high">
小部件的懒惰和安全初始化
js const Widget = React. lazy(() => import('./Widget'));
function Section() {
const inView = useInViewport('#sec');
return <div id="sec">{inView? <React. Suspense fallback={null}><Widget/></React. Suspense>: null}</div>;
}
稳定布局
css
.hero {
content-visibility: auto;
contain: layout paint;
contain-intrinsic-size: 720px 320px ;/LCP reserve/
}
12)回归控制和预算
Bundle-budget:通用JS ≤ N kB,CSS ≤ M kB,initial-chunk ≤ KB。
CI(模拟)+RUM-alerta中的Web-Vitals(在感应器上)。
乐队分析:PR中的source-map-explorer/分析仪。
组件的性能基准(10k元素渲染,反应时间)。
13)反模式
在第一个屏幕上装载"全部和一次":图形,编辑,地图。
巨大的全球州→级联的重塑。
图像大小为2-4 ×,没有"srcset/sizes"。
主流上的长同步循环。
"outline: none"和定制焦点没有优化-干扰渲染指标。
通过"顶部/左侧"进行动画(打破布局并调用reflow)。
14)屏幕支票清单
[] LCP ≤ 2.3 G/mobile流量为5 c,CLS ≤ 0。1、INP ≤ 200毫秒
- 关键资源:优先事项/优先事项;剩下的是defer/lazy
- 乐队:代码分裂,没有额外的依赖性
- 清单/表虚拟化,重小部件延迟初始化
- 图片:AVIF/WebP,"srcset/sizes","loading="lazy""
- 字体:variable+'font-display", preload只需要
- CSS:关键直线、Purge、"内容可视性"和"contain"在适当的情况下
- 重型计算的工人/空格
- 预算和Web-Vitals连接到dashbords/alert
15)实施计划(3次迭代)
迭代1-快速胜利(1-2周)
包括Brotli/HTTP-2/3,CDN。资源的关键性CSS和前端LCP。
将重型小部件带入动态导入。
图像→ AVIF/WebP+"srcset"。字体:"字体显示:交换"。
迭代2-结构改进(3-4周)
路线上的代码分裂,乐队分析,删除"重型"自由。
列表虚拟化,内容可见,contain-intrinsic尺寸。
实施SSR/流媒体/岛屿(相关)。
带有Web-Vitals的RUM,预算为CI。
迭代3-规模和可持续性(连续)
Workers/offscreen-canvas,计算战斗,startTransition/deferredValue。
定期进行笔试,回归的自动摘要,团队培训。
16)迷你常见问题
什么是移动上最快的速度?
减少原始JS,SSR/流媒体和 LCP图像优化。
总是需要SSR吗?
没有。如果页面动态交互且缓存不佳-islands/partial hydration可能会更好。
为什么INP在"轻型"帮派下表现不佳?
可能是主线程上的冗长任务(排序、图形)-在Worker中分解任务。
底线
快速UI是一组学科:网络优先级和缓存,较小和较晚的乐队,可预测的无跳跃渲染,经济的图像和字体,以及在现实世界中的持续度量控制。输入预算,自动化检查,教导团队在每个步骤中考虑速度-因此界面将在今天和一年后保持快速。