Tsonglew's Blog

Back

“帮我优化前端性能”范围太大。AI 可以压图片、加缓存,也可以给每个组件套一层 memo。我们要指定需要改善的用户操作,并要求它留下能支持结论的测量记录。

这一讲继续使用慢读。实验页提供同步和分批计算,方便观察主线程忙碌时页面的反应;静态基线用于检查初始加载。源码见下载包,优化请求模板见提示词文件

当前资料库只有三条固定资料,不能据此判断大列表的实际性能。文中的阈值来自 web.dev,具体实验结果由你的设备产生,我们不预填一组“优化前后”的成绩。

先挑一个有证据的症状。第一次打开等待很久,就检查资源与首屏;数据增加后输入卡顿,就录制那次交互;新增编辑组件后脚本明显变大,就比较构建产物。先用实验室学习测量,再把方法用在自己的原型。没有重现慢,就记录当前基线,不必为了完成课程制造一项优化。

先选一段用户操作,再确定测什么#

慢读至少有三段值得单独记录的使用过程。

使用过程用户关心的结果可用的测量
首次打开资料库主要内容多久可见,页面是否跳动LCP、CLS、资源瀑布
输入关键词并选择资料输入有无迟滞,点击是否及时反馈交互录制、主线程任务、INP
搜索后等待完整结果结果何时稳定显示,是否属于当前查询自定义业务耗时、请求时序、正确性检查

INP 关注交互到下一次绘制的响应速度。一次点击之后,后台请求多久完成,是另一项业务耗时。页面可以及时显示加载状态,但服务器仍然要花时间返回结果;这两件事需要分别记录。web.dev 的 INP 定义

开始录制前,把环境写下来。至少包括提交版本、生产构建方式、设备与浏览器版本、视口、CPU 和网络限速、数据规模、缓存条件。固定测试时的电源状态,关闭会争抢资源的无关程序。开发服务器上的热更新和额外检查会影响结果,正式比较要使用生产构建。

冷缓存首次访问和热缓存再次访问分别跑。切换网络档位之后,基线也要在同一档位重新测;不要把一次真机结果与另一次桌面模拟结果放进同一组比较。

真实用户指标和本地录制,各自回答一部分问题#

Core Web Vitals 当前包含下面三项。表中的“良好”要按真实访问的第 75 百分位,也就是 p75 评估,并把移动端与桌面端分开统计。

指标主要观察什么良好阈值
LCP视口中主要内容的呈现时间≤ 2.5s
INP页面交互的响应情况≤ 200ms
CLS意外布局偏移的累计程度≤ 0.1

三项各自达到对应阈值,才算通过这一组评估。CLS 没有毫秒单位。没有交互的访问也可能没有 INP,缺失值不能补成零。指标口径与设备分类见 Web Vitals 官方说明

真实用户数据来自 RUM,也就是页面上的真实用户监测,或符合条件的网站在 CrUX 中的汇总。它能反映不同设备与实际操作。查看报告时还要确认是当前 URL 的数据,还是整个来源站点的数据。小站没有足够公开样本时,应明确“缺少真实用户数据”,继续做本地诊断,不能宣称已经通过线上 p75。

实验室数据用于复现和定位。Chrome Performance 可以记录你执行的一段交互;Lighthouse 常规导航测试侧重一次加载,没有完整的真实用户操作过程。它的 TBT 是实验室阻塞时间指标,可以帮助发现响应性风险,不能替代 INP。一次本地录制也不代表线上用户的分布。web.dev 的实验室交互诊断说明

本课先在 Performance 中录一次加载,再单独录一次“输入关键词、点击资料”的操作。保留原始 trace,用它核对 AI 声称的阻塞位置。Chrome 的性能录制指南

把 LCP 拆成四段,找到时间花在哪里#

在 Performance 中选中 LCP 对应的元素,再看它依赖的请求。慢读静态页以文字为主,实际 LCP 元素要由工具确认。下面以需要下载图片的 LCP 为例,拆解从导航到绘制的过程。

阶段起止位置优先核对的证据
TTFB导航开始到 HTML 响应首字节重定向、连接与服务端等待
资源加载延迟HTML 首字节到 LCP 资源开始请求资源何时被发现,有无脚本或接口串行等待
资源加载时长LCP 资源开始请求到下载完成传输体积、缓存和网络条件
元素渲染延迟资源可用到 LCP 元素绘制主线程任务、阻塞样式、字体与显示条件

图片型 LCP 从导航到绘制的四段等待,包括 TTFB、资源加载延迟、资源加载时长与元素渲染延迟

图中展示先后顺序,各段长度不代表实际耗时。

系统字体呈现的文字可能不需要单独下载 LCP 资源,对应的资源阶段可以为零。TTFB 也包含到服务器途中的开销,不能直接当作后端处理时间。web.dev 的 LCP 分段方法

假设后续给慢读加了资料封面,这张封面成为 LCP。若瀑布图显示它要等一大段 JavaScript 执行后才开始请求,先处理资源发现太晚的问题。只压缩文件体积,会缩短下载阶段,前面的等待仍然存在。

请求已经早早完成,画面却很晚出现,就继续检查它是否被加载状态隐藏,以及主线程是否一直在忙。让 AI 写出“我认为这一段时间会减少”的假设,再改一处实现,录制相同过程验证。四段的变化要一起看,时间也可能从下载阶段转移到渲染等待阶段。

用同一份计算,观察主线程怎样影响操作#

打开实验页的性能区域,保持标签页在前台,选定同一计算量,再依次运行“运行同步计算”和“运行分批计算”。两种模式执行同一份整数校验和计算,页面显示总耗时、最长工作片段和校验和,还会比较两种结果是否一致。改变计算量后,需要重新运行两种模式。

同步模式把工作集中在一个执行过程里。分批模式大约工作 8ms 后通过 setTimeout 把后续工作安排到另一个任务,让浏览器有机会处理其他工作。运行期间可以试着点击“点我计数”,同时观察页面的心跳次数。

不要预设分批一定让总耗时更短。调度有开销,分批的目标是减少连续占用主线程的时间,让页面有机会响应。先比较校验和,确认工作没有少做;再看最长片段、计数按钮的反应和 Performance 录制。心跳次数还受总运行时间影响,不能单独用次数多少给方案排优劣。

实验页面用 performance.now() 计时,总耗时包含分批等待,不包含启动前留给页面绘制的等待;最长片段只测计算函数的执行区间。它们都不能直接叫 INP,也不能替代浏览器记录的长任务。

浏览器把超过 50ms 的主线程任务称为长任务。任务执行时会占用主线程,其他工作需要等待;50ms 是诊断阈值,短于它也不意味着每一帧都来得及完成。实践中应按目标设备继续缩短关键片段。await Promise.resolve() 只切到微任务,不能可靠地给渲染和输入让出机会。分任务或合适的调度 API 才能改变这种连续占用。web.dev 的长任务优化指南

如果真实录制显示慢读在每次输入时遍历大量资料,先分清耗时发生在筛选计算,还是几千张卡片的 DOM 更新与布局。计算昂贵可以考虑减少重复工作,必要时放进 Worker;DOM 过多可以分页,或在需求合适时做虚拟列表。虚拟列表还要验证焦点、键盘导航与辅助技术,不能只看滚动变快。

给函数加上 async 不会把同步循环搬离主线程。大量计算仍在当前线程执行,Worker 才涉及另一种执行环境,数据传递也有成本。MDN 对 async 函数执行方式的说明

图片、字体和 JavaScript,按证据决定先改谁#

下面这些改法各有适用条件。让 AI 先指出当前记录支持哪一行,再选择行动。

证据可以尝试的修改要回看什么
图片传输占首屏时间的大头提供符合显示尺寸的图片,使用合适格式与响应式资源图像质量、实际下载尺寸、LCP 阶段变化
LCP 图片发现或调度过晚让它能被初始 HTML 发现,必要时提高关键资源优先级请求开始时间,是否挤占其他关键资源
字体下载阻塞文字或替换后跳动减少字重和字符范围,选择合适 font-display 与回退字体度量文字可见时间、字体替换后的 CLS
大量脚本在首屏下载和执行按路由或功能拆分,推迟暂不需要的编辑器和图表初始 JS 传输、执行时间、首次使用该功能的等待
图片或异步内容出现时页面位移预留宽高或合理比例,保留稳定的内容区域Layout Shift 记录中的受影响元素

LCP 图片不应随意懒加载,屏幕外的非关键图片才适合延后。为每个资源都提高优先级,也无法表达哪个更重要。字体使用 swap 能减少文字不可见的等待,但回退字体与目标字体的尺寸差异仍可能带来偏移。字体优化建议CLS 调优说明

拆包要看用户过程。慢读的可选编辑器可以等用户进入编辑功能后加载,搜索框却不能在用户按下第一个键时才等待一串依赖。每次延后资源,都要补测第一次使用那项功能的体验。web.dev 的 JavaScript 拆分指南

React 的 memo 要能说出省了哪次工作#

如果后续用 React 实现慢读,输入卡顿时先录制,再打开 React 的分析工具,查看哪些组件渲染、耗时在哪里。输入状态放在过高层级、Effect 引发连续更新,都会让小操作影响很大的组件树。

memo 尝试跳过 props 没变的组件渲染,组件自己的状态或读取的 context 变化时仍会渲染。useMemo 缓存依赖未变时的计算结果。若每次都传入新对象,或者搜索关键词本来就每次变化,缓存能省下多少需要实际验证。它们不会修复慢 API,也不会消除所有布局工作。

先修正确性,再讨论缓存。自定义比较函数也要付出比较成本,并保证没有忽略会改变行为的输入。用了 React Compiler 的项目还应先确认已有自动优化,避免堆重复措施。React 的 memo 参考useMemo 的适用条件

保留前后记录,给 AI 一个可检查的交付要求#

同一设备、同一网络条件和同一份数据,优化前后各执行相同流程至少五次,保留每次原始结果,为每个指标分别计算中位数。五次是这份练习的操作约定,不能据此估计真实用户 p75。条件允许时交替运行两个版本,减少热机、温度变化等时间因素的影响。遇到异常的一次,保留记录并查原因,不要只挑最好看的数值。

下面的记录表故意留空,测完再填。

项目优化前优化后
提交版本与构建方式待记录待记录
设备、浏览器、限速和缓存状态待记录待记录
场景、数据规模和操作步骤待记录待记录
五次原始测量与中位数待记录待记录
trace 文件或报告位置待记录待记录
正确性与交互验收待记录待记录

还可以给慢读订一个项目预算。例如在约定网络下,列表页初始传输的压缩后 JavaScript 不超过 100KiB,某个非关键功能新增依赖超过 20KiB 就触发人工检查。这是教学用的项目预算,绝非通用标准,也不能替代 CWV。确定口径时要写明路由、压缩方式与测量边界。

把下面的提示词与原始记录一起交给 AI。

如果两组结果接近波动范围,就写“本次未证明有稳定改进”,保留记录或撤销改动。一次优化结束时,读者应该能够重跑同一过程,得到足以支持判断的证据。

将原始测量、正确性回归和取舍写入交付工作簿,再检查最后一页的交付清单。练习包的本地实验、你生成的交互原型和真实线上产品分别验收;后两者中尚未实现的能力继续标明。

后端转全平台 06|性能调优,让 AI 用测量结果证明改进
https://tsonglew.github.io/blog/backend-to-platform-04-performance
Author Tsong Lew
Published at 2026年9月23日