先给结论:当同一 URL 对移动端与桌面端、或对已登录与未登录用户返回不同 HTML 时,不能把两份响应直接放在一起比较速度,而要先判断差异是“服务端分叉”还是“前端补丁”。如果两份响应的首字节内容不同,必须按变体分别采样;如果只是同一份 HTML 在客户端被脚本改写,则应以原始响应为基准,再单独记录脚本执行后的结果。判断错了,后续的优化动作会打偏。
把同一地址的多次请求结果摊开,第一步不是看耗时,而是看响应体从哪里开始不同。用命令行抓取时,可以把响应头与正文分别存下来:
curl -s -D headers-mobile.txt -o body-mobile.html -A "移动端UA字符串" https://example.com/page
再用不带该 UA 的命令抓一份桌面版,用带登录 Cookie 的命令抓一份登录态版本。三份文件用 diff 比较。如果差异出现在 <head> 或正文主体,说明服务端按设备或身份做了分叉;如果正文完全一致、只有脚本在运行后改变了 DOM,那对照的焦点就应该放在脚本加载与执行顺序上。
判断依据是响应体差异的位置,而不是耗时数字的大小。耗时差异可能来自网络抖动、缓存命中与否、CDN 节点不同,单独用它推断“某个变体更慢”并不成立。
确认是服务端返回不同内容后,两种做法都成立,但适用条件不同。
做法一:为每个变体单独采样。适用于变体数量少且稳定的情况,比如只有移动端和桌面端两套模板。动作是固定 UA、Cookie、请求头,对每个变体各抓若干次,记录首字节时间、HTML 大小、关键资源数量。结果是你能得到每个变体的独立基线,后续任何改动只跟对应基线比。代价是维护成本随变体数量增加,登录态版本还依赖测试账号的有效性。
做法二:只对公共部分建立基线,变体差异单独标注。适用于变体多、但大部分结构共享的情况,比如同一模板按地区或登录状态插入不同模块。动作是先确认哪些资源在两个变体中都存在,只对这些公共资源做速度对照,再把各变体独有的脚本或样式单独列出。结果是基线更稳定,不容易被单一变体的波动干扰。代价是容易漏掉“只在某个变体里才加载的重资源”,而那恰恰可能是真实用户感知到的瓶颈。
选择条件可以这样把握:如果变体之间的关键渲染路径资源重合度高,用做法二;如果变体各自加载了完全不同的脚本或样式,用做法一。两者可以并存,但不要用一套基线去覆盖所有变体。
已登录与未登录返回不同内容,往往涉及个性化模块、用户数据接口和权限判断。这类差异的对照有一个额外前提:登录态请求必须使用可重复的测试身份,且该身份的权限范围要记录清楚。否则今天用 A 账号、明天用 B 账号,响应差异可能来自权限不同而非性能变化。
实际操作中,可以把登录态请求的 Cookie 单独保存,并在每次对照时确认 Cookie 仍然有效。如果 Cookie 过期,请求会跳转到登录页,此时抓到的响应与页面本身无关,对照结果无效。这一步容易被忽略,因为跳转后的页面通常也能正常返回,只是内容完全不同。
一个假设的例子:某页面未登录时返回约 80KB 的 HTML,登录后因为插入用户菜单和推荐模块变成约 140KB。如果只测未登录版本,会低估真实登录用户的首屏解析时间。此时应把两个版本分别记录,并注明差异来自服务端插入的模块,而不是网络或缓存问题。
这三种情况的共同点是:现象看起来像性能差异,但合理解释可能是缓存、工具参数或跳转,而不是页面本身变慢。在排除这些解释之前,不要急着改代码。
如果服务端返回的 HTML 完全相同,差异只出现在脚本执行之后,那么按设备或登录状态分组对照就没有意义,因为服务端没有分叉。此时应该对照的是脚本的加载与执行时机:同一份 HTML 在不同设备上,可能因为 CPU 性能或网络条件不同,导致脚本完成时间不同。动作是记录脚本开始执行和 DOM 稳定之间的时间差,而不是比较 HTML 传输时间。结果是你能区分“传输慢”和“渲染慢”,两者的优化方向完全不同。
最后一步是记录结论并限定适用范围:每个变体的基线只在相同的 UA、Cookie、缓存状态和网络条件下有效。条件变了,基线需要重新采样。这一步做完,才能决定下一步是优化服务端模板、调整脚本加载顺序,还是先解决重定向链。