fid必须用javascript主动监听计算上报,不能靠html控制;其本质是用户触发事件到浏览器开始处理的排队延迟,需用event.timestamp减performance.now()近似计算,且监听须尽早注册并过滤后台事件。

FID 不能靠 HTML 标签或属性“实现”,它压根不是 HTML 能控制的指标——必须用 JavaScript 主动监听、计算、上报,且所有优化动作都发生在 JS 加载与执行阶段。
为什么 performance.getEntriesByName('first-input') 拿不到真正的 FID
这个 API 返回的 duration 字段是事件回调执行耗时,不是排队延迟。FID 的本质是「用户点了,但主线程正忙,得等它空下来才开始干活」那段空等时间。而原生 Entry 对象没暴露 processingStart 这个关键时间点,所以直接读 duration 会把处理时间误当延迟,数值完全失真。
- 真正有效的 FID =
event.timeStamp(触发时刻) − 浏览器开始处理该事件的时刻 - 后者无法直接获取,只能用
performance.now()在监听回调里近似替代(前提是监听器本身不被阻塞) - 若监听器本身挂起在长任务队列末尾,那算出来的差值就偏大——所以监听必须尽早注册(
内或DOMContentLoaded后立刻)
手动监听 click/keydown/mousedown/touchstart 的实操要点
只认这四类事件;滚动、缩放、hover、无绑定的空点击都不算;页面在后台(document.hidden === true)时触发的也剔除。
- 在
最顶部插入内联脚本,用addEventListener绑定上述四类事件,捕获模式设为true(确保能抢在其他监听器前拿到事件) - 每个回调里立即执行:
const delay = event.timeStamp - performance.now(),注意这是近似值,不是绝对精确,但足够反映主线程阻塞程度 - 只取第一个有效事件:一旦得到
delay > 0 && delay 的结果,立刻调用 <code>removeEventListener清掉所有监听器 - 别忘了加后台校验:
if (document.hidden) return,否则切到其他标签页再点,数据就污染了
用 first-input-delay 库省掉边界坑
npm 包 first-input-delay 已封装好事件过滤、后台判断、单次上报、异常阈值丢弃等逻辑,比手写更稳。
- CDN 引入方式(推荐用于快速验证):
<script src="https://unpkg.com/first-input-delay@4.0.0/dist/first-input-delay.min.js"></script>
- 注册回调:
perfMetrics.onFirstInputDelay((delay, evt) => {<br> if (delay >= 0 && delay // 上报 delay 和 evt.type,例如发给 Sentry 或自建埋点<br> }<br>}); - 它内部已避开
setTimeout或 Promise 微任务延迟,确保监听器在宏任务最前端执行,降低自身引入的误差
优化方向全在 JS 执行阶段,和 HTML 写法基本无关
HTML 文件本身对 FID 几乎没直接影响。真正拉高 FID 的,是 JS 包体积大、解析慢、执行久、没拆分长任务。
- 优先砍掉首屏非关键 JS:把
analytics、AIGC插件、第三方 UI 组件的初始化延迟到交互后或空闲时(requestIdleCallback) - 用
type="module"+defer控制加载顺序,避免阻塞解析;对大型依赖考虑import()动态导入 - 检查是否有同步 DOM 操作+大量计算混在一起的长任务,用
queueMicrotask或setTimeout(..., 0)拆成小块 - 别忽略 Web Worker:把 canvas 渲染、JSON 解析、加密等 CPU 密集型操作移出主线程
真正容易被忽略的是:FID 已于 2024 年 3 月被 INP 正式取代,但 CrUX 和多数 RUM 工具仍在回传 FID 数据。你现在做的所有 FID 优化,其实是在为 INP 做前置准备——因为两者共通解法就是「不让主线程长时间卡死」。盯着单个数字没用,得看真实设备上 75th percentile 的分布。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











