找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4771

积分

0

好友

621

主题
发表于 昨天 22:08 | 查看: 15| 回复: 0

今天顺手给 agent101 跑了一次 PageSpeed Insights。

桌面端 88,移动端 58。

58 那个橙圈往那儿一摆,挺刺眼。但我看完报告之后的第一反应不是“完了要重构”,而是——这 58 分丢得特别蠢

蠢到什么程度:整个页面真正慢的原因只有一个,藏在一个我从来没打开看过的文件里,一行。

先立个靶子。很多人跑完 PageSpeed 就干两件事:看那个大圆圈,然后把报告里飘红的条目从上到下挨个改一遍。

这两件事都不太对。

分数是五个指标的加权平均。你不知道权重,就不知道该先修哪个。更要命的是,报告告诉你的是“哪儿慢”,不是“为什么慢”——这中间隔着一段得自己动手挖的路。

我这篇就按这个顺序来:先把指标讲明白(每个测什么、及格线多少),再回头看我这份报告,然后把我一路挖到根因的过程原样写出来,包括我猜错的那几次。

尽量不说行话。

PageSpeed 性能诊断报告截图


一、先说清楚:那个分数是怎么算出来的

这是最该先知道的一件事,但几乎没人一上来就讲。

Lighthouse 的性能分不是什么综合评价,它就是五个指标按固定权重加权平均出来的一个数:

指标 权重 一句话说它管什么
TBT(总阻塞时间) 30% 页面卡不卡
LCP(最大内容绘制) 25% 主要内容多久出来
CLS(累积布局偏移) 25% 页面跳不跳
FCP(首次内容绘制) 10% 第一眼多久有反应
SI(速度指数) 10% 内容填充得快不快

(这是目前主流版本的权重,Google 改过几次,以你自己跑出来的那版为准。)

看出问题没有——

TBT 一个指标就占 30%,比 FCP 和 SI 加起来还多一倍。

意思是如果你 JS 写得很重、主线程被长任务卡住,你在最贵的那 30% 上直接归零,后面怎么压图片都救不回来。

反过来,TBT 和 CLS 都拿满,你手里就已经攥着 55 分的基本盘。

记住这个数:TBT + CLS = 55%。这是性能分的胜负手。

PageSpeed 性能分数构成与五项指标权重


二、这些指标,挨个说人话(附及格线)

已经很熟的可以跳到第三节看我的报告。

我按用户打开一个页面的时间顺序排,比按权重排好理解。

TTFB(首字节时间) 你点了链接,服务器把第一个字节吐回来用了多久。测的是服务器和网络,跟前端代码基本无关。及格线:800ms 以内算好。

FCP(首次内容绘制) 屏幕上出现第一个内容——第一行字、第一个色块。用户的感受是“哦,它动了”。及格线:1.8 秒以内算好,超过 3 秒算差。

LCP(最大内容绘制) 首屏里最大的那块东西画完的时刻,通常是主视觉图或者大标题。用户的感受是“加载完了”。及格线:2.5 秒以内算好,超过 4 秒算差。

这个是三大核心指标之一,也是 SEO 真在意的那几个之一。

SI(速度指数) 不看某个时间点,看整个加载过程里屏幕被“填满”的速度。可以理解成 FCP 和 LCP 的连带结果。及格线:3.4 秒以内算好。

TBT(总阻塞时间) 从 FCP 开始,主线程被超过 50ms 的长任务占住的总时长。说白了就是:这段时间你点什么都没反应。及格线:200ms 以内算好。

CLS(累积布局偏移) 加载过程中内容乱跳的累计量。就是那个经典体验:手指刚要点“取消”,上面一张图加载出来把按钮顶下去了,你点成了“确认”。及格线:0.1 以内算好。

INP(交互到下一次绘制) 不在 Lighthouse 打分里,但它 2024 年 3 月已经替掉 FID,成了官方三大核心指标之一。测的是真实用户点击后页面响应的延迟。及格线:200ms 以内算好。

TBT 和 INP 什么关系?TBT 是实验室里观察主线程阻塞情况的指标,和真实用户 INP 有关联,但不能互相替代。 TBT 高,真实用户的 INP 通常也需要重点关注。

网页性能指标时间轴与推荐阈值科普图


三、我的 58 分,是怎么丢的

先说一句报告上很多人被绕过的话。

我这份报告最上面写着「无任何数据」。

PageSpeed 其实是两套数据拼起来的:上半部分是真实用户数据(CrUX),来自全世界真正访问过你站的 Chrome 用户,这个才是 Google 拿去参考的;下半部分是实验室数据,Google 在自己机房里模拟一台设备跑一遍。

我站太新,访问量没到采样门槛,所以上半部分是空的,只有实验室数据。

那实验室长什么样?报告角落写得很清楚:模拟 Moto G,低速 4G 节流

一台中低端安卓机,一条被掐过的网。

它不是在测“我的电脑连宽带打开有多快”,是在测“一个网络不好的普通安卓用户打开有多慢”。

好,看数据。被测的是 agent101 首页,一个 Next.js 做的 AI Agent 术语站:

指标 移动端 桌面端 及格线
FCP 7.7s 🔴 1.6s < 1.8s
LCP 8.0s 🔴 1.6s < 2.5s
SI 7.7s 🔴 1.6s < 3.4s
TBT 0ms 🟢 0ms < 200ms
CLS 0.001 🟢 0 < 0.1
性能分 58 88

把第一节那张权重表拿过来一对账,58 分怎么来的一目了然:

TBT 0 毫秒满分,CLS 0.001 满分。 这俩加起来 55%,我全拿了。

FCP 7.7s、LCP 8.0s、SI 7.7s,全部远超“差”的判定线。 这仨加起来 45%,我几乎全丢。

55 + 一点零头 ≈ 58。

这份报告最有意思的地方在这:我的页面一点都不卡。

TBT 是 0,主线程几乎没被阻塞过。CLS 接近 0,加载过程稳得很。DOM 只有 390 个元素、深度 10,很轻。

TTFB 只有 20 毫秒。 服务器快得离谱。

一个服务器 20ms 响应、主线程不卡、布局不跳的页面,首屏花了 8 秒才画出来。

那 8 秒它在干嘛?

移动端 58 分与桌面端 88 分指标对比分析


四、往下挖:根因是一行 @import

报告说是 Google Fonts 阻塞了渲染,还给了个数:移动端移除这条阻塞,预计缩短 7390 毫秒

7.4 秒。8 秒的 LCP 里有 7 秒多在等字体。

但报告到这儿就停了。它只说“Google Fonts 阻塞”,没说这东西是从哪儿来的。

我第一反应是:那我去 HTML 头部把那行 <link> 挪一下不就完了。

结果一看,HTML 里压根没有 Google Fonts。

curl -s https://agent101.gao-fei.com/ > home.html
grep -oE 'fonts\.(googleapis|gstatic)\.com[^"]*' home.html

空的。

整个首页就一个样式表 ec91b116b7009434.css,一个图片 preload,一个 script preload。没有字体,没有 preconnect,什么都没有。

那字体从哪儿来的?只可能在 CSS 里。翻开那个 CSS,第一行

@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&family=Noto+Sans+SC:wght@400;500&display=swap");

找到了。

这是字体能待的最坏的位置。

写在 HTML 的 <link> 里,浏览器扫描 HTML 时就能看见它,可以提前发请求,跟别的资源并行。

写在 CSS 的 @import 里,链条变成串行的四跳:

HTML  →  app.css  →  fonts.googleapis.com/css2  →  一堆 woff2

浏览器必须先把 app.css 下完、解析完,才知道还得去 Google 要东西。而这中间每一跳都是一次完整的 DNS 查询 + TLS 握手 + 往返。

这也顺带解释了我为什么没加 preconnect——HTML 里根本看不见 fonts.gstatic.com 这个域名,谁会想到去给一个自己看不见的域名建连接。

报告能告诉你哪儿慢。为什么慢,得自己 curl 一下。

CSS 中 @import 导致四跳串行字体加载阻塞


五、我明明加了 font-display: swap

回去看那行 @import 的结尾:&display=swap

加了。一直都加着。

那为什么 FCP 还是 7.7 秒?

这个地方我卡了一会儿,也是这次整个排查里我觉得最值钱的一个收获:

font-display: swap 和“渲染阻塞”,是两件完全不同的事。九成的性能文章混着讲。

swap 管的是:字体文件没到的时候,字要不要先用系统后备字体显示出来。

它管不了:字体的那个 CSS 文件没到的时候,页面敢不敢开始画。

浏览器的逻辑是——我看见一个样式表引用,我不知道它会不会改变页面长相,那我就先不画,等它下完再说。

@import 引进来的那个 css2?family=...它本身就是一个样式表

所以顺序是这样的:

  1. 浏览器等 app.css(阻塞)
  2. 解析 app.css,发现 @import,去要 Google 的 CSS(继续阻塞)
  3. Google 的 CSS 回来了,swap 这时候才开始生效
  4. 但第一屏早就该画完了

swap 在第三步才上场。而我丢的那 7 秒,是在第一步和第二步里丢的。

swap 救的是字,救不了页面。

这一条我原本的草稿里写的是“去加 font-display: swap”。要不是动手 curl 了一次,我就把一条我早就做过的事当成建议写出去了。

font-display swap 与渲染阻塞的关系图解


六、800KB 的中文字体,用户手机里本来就有

接着挖。我在本机开 DevTools,勾上 Disable cache,挂上 Slow 4G 节流,刷新,筛 Font。

底下那行数字:

17 / 34 requests934 kB / 1,381 kB transferred

翻译一下:整页 34 个请求,17 个是字体。整页传输 1.38 MB,字体占 934 KB,七成

再看列表。14 个文件名长得一模一样,只有末尾编号不同:

k3kXo84MPvpLmixcA63oeALhLOCT-...V.102.woff2   60.8 kB
k3kXo84MPvpLmixcA63oeALhLOCT-...V.105.woff2   53.1 kB
k3kXo84MPvpLmixcA63oeALhLOCT-...V.110.woff2   61.6 kB
...

这是 Noto Sans SC。中文字多,Google Fonts 会按 unicode-range 把它切成上百个子集,浏览器用到哪几个字就下哪几个文件。听着挺聪明,但落到我这儿就是一次拉了 14 个包,800 多 KB

Time 列更能说明问题:从 2.85 秒一路排到 5.98 秒,十几个请求密密麻麻挤在 5 到 6 秒那一段。它们不是排队一个个下的,是全都卡在同一条起跑线后面——得先拿到 Google 的 CSS,才知道要下哪些子集。

而我的 DOMContentLoaded 是 2.47 秒

DOM 两秒半就准备好了,第一个字体 2.85 秒才落地,最后一个拖到将近 6 秒。中间这三秒半,页面就在那儿干等。

这就是报告里“LCP 主要耗时在元素渲染延迟”那句话的真身。

然后我去看了 DevTools 的 Rendered Fonts,这一眼最扎心:

Noto Sans SC — Network resource
Inter        — Network resource

Network resource,意思是走网络下的。

可我的 font-family 是这么写的:

font-family: Inter, -apple-system, BlinkMacSystemFont,
PingFang SC, Noto Sans SC, Microsoft YaHei, sans-serif;

PingFang SC 是 iOS 和 macOS 自带的。Microsoft YaHei 是 Windows 自带的。安卓那边系统字体本来就是 Noto 系。

每一个访问我这个站的中文用户,设备里都已经装着一套能用的中文字体。

800 KB,将近四秒等待,只为了让用户看到一份他手机里本来就有的字。

(还有个我没搞明白的:Rendered Fonts 那儿显示的是 Noto Sans SC Thin,但我 @import 里只请求了 400 和 500,压根没要 Thin。这个先记着,等下一篇搞清楚了再说。)

800KB 中文字体与系统自带字体加载对比


七、修复清单,和我的排期

按性价比从高到低排。可以直接抄。

第一刀,砍掉 Noto Sans SC。

这一刀最狠,省 800 KB,直接砍掉 14 个请求。中文交给系统字体,PingFang SC / Microsoft YaHei 在真实设备上的表现跟 Noto 差别很小,不值 800 KB。

/* 改前 */
@import url("...&family=Noto+Sans+SC:wght@400;500&display=swap");

/* 改后:只留拉丁字体和等宽 */
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap");

顺手把 JetBrains Mono 的 700 也砍了,我数了一下代码块里没用到粗体等宽。

第二刀,把字体从 @import 里搬出来。

搬到 HTML 头部,四跳变三跳,而且能跟别的资源并行:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap">

fonts.gstatic.com 那行的 crossorigin 不能漏,漏了这条 preconnect 白建。字体是匿名跨域请求,不带这个属性,浏览器会另开一条连接,等于没提前。

第三刀,自托管,第三方请求归零。

Next.js 直接用 next/font,构建期把字体下到自己域名下,自动 preload,自动做 fallback 字形匹配(这个能顺手把 CLS 再压一点):

import { Inter } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  weight: ['400', '500', '600'],
  display: 'swap',
})

这一步做完,前两刀都不用了。

第四刀,关掉多余 polyfill。 报告揪出来打包了 Array.atflatflatMap 这些现代 API 的垫片,白浪费 11.6 KB。调 browserslist,别再为 2018 年的浏览器编译:

"browserslist": ["last 2 versions", "not dead", "> 0.5%"]

我自己的排期:

第一刀和第二刀今天就上,成本半小时,改两个文件。这两刀下去,按报告给的估算,移动端那 7.4 秒能收掉大半。

第三刀放下个迭代,因为顺带要处理等宽字体的子集化,麻烦一点。

第四刀跟着下次依赖升级一起做。

改完我重新跑一遍 PSI,把前后对比发出来。这种带实测数字的前后对比,比任何优化教程都直观。

页面性能修复清单与排期


八、顺便说说那个 96 分和那两个 100 分

性能之外还有三个维度:无障碍 96,最佳做法 100,SEO 100。

看着漂亮,里面有两个坑。

坑一:96 分的无障碍,扣在“高级灰”上。

主要扣分项是文本对比度不足,具体是我那个 .text-ink-mute 类——代码说明、时间戳、标签、备注,全是小字配浅灰。

典型的设计审美和可读性打架。设计稿上看着克制、有呼吸感,但对视力一般的人、对户外强光下看手机的人,那几行字基本等于不存在。

还有一个更隐蔽的:我好几个链接的文字都是「详情 →」。

视觉上没问题,你能看见它在哪张卡片里。但屏幕阅读器用户会听到一连串“详情、详情、详情”,完全不知道每个链接跳去哪。

报告里还有 10 项标着“需要人工复核”——键盘导航、自定义控件焦点、ARIA 标签这些,自动化测不了,得自己拿键盘 Tab 一遍。这活儿我还没干。

坑二:最佳做法 100 分,不等于安全。

这个特别容易让人麻痹。100 是满分,但报告下面那堆不计分的诊断项里写着一串缺失:

  • 没有 CSP(缺 script-src),XSS 防线敞着
  • 没有 COOP 响应头
  • 没配 Trusted Types
  • HSTS 配了,但缺 includeSubDomainspreload

Lighthouse 只给它认识的项打分,不给你的风险打分。 满分的意思是“我查的这几项你都过了”,不是“你安全了”。

这几条对个人项目优先级不高,但心里得有数,而不是看见 100 就觉得稳了。

无障碍 96 分与最佳做法 100 分不等于零风险


九、报告里那个新东西:智能体浏览 2/2

最后这块是今年的新玩意儿,不少人跑报告时看到过但没细看。

分数栏里性能、无障碍、最佳做法、SEO 后面,多了第五个:智能体浏览。它不给 0 到 100 的分,只给一个比值。我这次是 2/2。

这是 2026 年新增的实验性类别,目前官方仍把它定位为探索中的能力,标准也还在演进。

它测的东西和前面四个都不一样——

前四项测的是用得爽不爽,SEO 测的是爬虫读不读得懂。

这一项测的是:一个 AI Agent 能不能读懂你的页面,并且在没有人操作的情况下把事办完。

目前主要看几样:

无障碍树干不干净。 Agent 不看你渲染出来的漂亮按钮,它读的是无障碍树,就是屏幕阅读器读的那套结构。按钮有没有名字、角色对不对、层级乱不乱,直接决定 Agent 能不能找到该点的东西。

布局稳不稳(CLS)。 这里换了个含义。对人来说 CLS 差是“手滑点错”,对 Agent 来说是它定位到了坐标,等它动手时按钮已经挪走了,于是点错

llms.txt。 放在域名根目录的一个 Markdown 文件,给 AI 工具一份站点导览。它目前不是正式标准,也不应被当成 SEO 加分项;如果维护成本很低,可以把它当作额外的机器可读说明。

WebMCP。 让页面把自己的能力以工具形式注册出去,让 Agent 直接调用而不是模拟点击。规范还在动,现在实现属于追一个移动靶。

我这次的 2/2,说白了不是因为我做了什么智能体适配,是无障碍做得还行、CLS 接近 0,白捡的。llms.txt 和 WebMCP 在报告里都标着待人工完善。

但这事有点意思。一个讲 AI Agent 的站,被一个 AI Agent 友好度的审计项打了满分,而满分的来源,是我当初为人类做的无障碍工作。

你为屏幕阅读器做的每一分努力,现在都自动折算成了 Agent 的可用性。

这可能是无障碍这件事这些年最实际的一次回报。

AI Agent 智能体浏览能力与无障碍关系图


最后说几句

回到那个公式:

TBT 30% + LCP 25% + CLS 25% + FCP 10% + SI 10%。

我的 58 分,本质是 55 分的基本盘,加上一条被一行 @import 拖垮的关键渲染路径。代码没毛病,服务器 20ms,DOM 干干净净。毛病在一个我从来没打开看过的 CSS 文件的第一行。

三条最实在的建议:

一、先看指标构成,别看总分。 打开报告第一件事是找五个指标里哪个红,然后对着权重算这个红的值多少分。有些红条目改完只涨一两分,纯属浪费周末。

二、报告只到“哪儿慢”,“为什么慢”得自己挖。 我要是信了报告那句“Google Fonts 阻塞”就直接去改 HTML,会发现 HTML 里根本没有 Google Fonts,然后原地懵掉。curl 一下,八个字符,省两小时。

三、中文站慎用 Google Fonts 的中文字体。 拉丁字体几十 KB,中文字体几百 KB 起步,而你的用户设备上早就有一套能用的了。这笔账在低速网络下特别不划算。

我这次的改动今天就上,preconnect 加上、Noto Sans SC 砍掉。改完重新跑一遍 PSI,前后对比我下次发。

性能优化最难的从来不是修,是知道该修哪个。而知道该修哪个之前,你得先愿意打开那个文件看一眼。


你的站跑出来多少分?评论区甩个数,最惨的那个我帮你看报告。




上一篇:STM32外设驱动AI生成验收:编译通过≠能上板,这5类坑要人工盯
下一篇:海鲜平台6k的Claude skill实测:Web实战与若依代码审计表现
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-17 12:46 , Processed in 0.601111 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表