fmp 已被 chrome 和 lighthouse 正式弃用,因其无法被一致、可靠地定义和测量;它依赖 dom 层级、语义标签(如 )、id/class 名称及内容密度等启发式规则估算“主角元素”,而 ssr 占位符或隐藏元素易导致数据失真,现应转向可精确监听的 lcp 指标。

FMP 已被 Chrome 和 Lighthouse 正式弃用,PerformanceObserver 从 6.0 版本起不再支持监听 "first-meaningful-paint" 类型事件——它不是“不重要了”,而是“无法被一致、可靠地定义和测量”。HTML 结构本身不会“决定”FMP 数值,但会直接决定哪些元素可能被算法选为“主角元素”,进而影响历史工具(如旧版 Lighthouse、WebPageTest)对 FMP 的估算结果。
为什么 <main></main>、<article></article> 或 ID 为 "content" 的容器会影响 FMP 估算
旧版 FMP 算法(如 WebPageTest 使用的 layout-instability + element weight 模型)依赖 DOM 层级与语义标签权重。它会扫描具备高“内容密度”特征的节点:文本行数多、图片尺寸大、位于视口中心区域、且有明确语义(<main></main> 权重 > <div>)、或匹配常见 ID/class 名(如 <code>id="content"、class="primary")。
- 若关键内容包裹在无语义的
<div id="app"> 中,且该节点初始为空,后续靠 JS 注入,则旧算法很可能将其忽略,转而选取顶部导航栏或 logo 作为“首个有意义内容”,导致 FMP 被严重低估 <li>若 <code><main></main>包含大量直出文本和一张<img>,即使图片加载慢,算法仍可能将<main></main>首次 layout 完成时间记为 FMP —— 因为它的 DOM 体积和位置权重足够高 -
<picture></picture>+<source></source>嵌套结构比单个<img src>更容易被识别为“核心媒体”,尤其当media属性匹配当前视口时 -
display: none的父容器内所有子元素完全被排除,哪怕子元素自己写了display: block -
visibility: hidden的元素仍参与 layout flow,若其内部有文本或图片,且父容器可见,它仍可能被计入 —— 这是常见误判来源 - CSS 动画中使用
opacity: 0但未设pointer-events: none,元素仍属“visible”状态,会被算法捕获 - 旧算法会把
"Loading..."文本当作“首个有意义内容”,导致 FMP 时间极早(比如 200ms),但实际用户看到的是空白或 loading 态 —— 数据失真 - 更糟的是,如果
<div id="loading"> 用了 <code>position: absolute覆盖主内容区,而主内容区初始opacity: 0,算法可能完全错过主内容,始终以 loading 文本为准 - 正确做法是:用
<template></template>或注释节点暂存 loading 状态,真实内容直出;或确保 loading 元素在 DOM 中位于主内容之后,且无文本内容(仅图标 SVG)
display: none 或 visibility: hidden 的元素是否计入 FMP 计算
不计入。FMP 估算只考虑已 layout 且 visible 的节点。但要注意边界情况:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
服务端渲染(SSR)HTML 中“占位符”写法对 FMP 估算的干扰
很多 SSR 模板会插入类似 <div id="loading">Loading...</div>,等 JS 加载后再替换。这种写法对 FMP 估算极为不利:
FMP 的本质不是技术指标,而是产品意图的映射。现在你应该关注 LCP —— 它可被 PerformanceObserver 精确监听,且强制要求目标元素必须在视口内、完成绘制、且尺寸最大。但如果你还在维护旧监控系统,那 HTML 结构里每一个 <main></main>、每一处 id="primary"、甚至每张 <img> 的 width/height 属性,都在悄悄左右那个已被废弃却仍在报表里显示的数字。










