应使用 PerformanceObserver 实时监听 resource 类型条目,过滤 initiatorType === 'link' 且 name 匹配 CSS 路径的记录;因 performance.getEntries() 是快照式调用,无法捕获页面加载后动态插入的 link 标签所触发的 CSS 加载条目。

直接用 PerformanceObserver 监听 resource 类型条目,过滤 initiatorType === 'link' 且 name 匹配 CSS 路径的记录,就能捕获动态插入的 <link rel="stylesheet"> 加载耗时——前提是插入发生在 performance.getEntries() 可见时间窗内,否则必须用监听模式。
为什么 getEntries() 常漏掉动态 CSS 加载
页面加载完成后才执行的 document.head.appendChild(link),其性能条目不会出现在初始 performance.getEntries() 结果里。浏览器只在资源实际发起请求后才创建 PerformanceEntry,而这个动作可能发生在 JS 执行中、用户交互后甚至定时器触发时。
-
performance.getEntries()是快照式调用,只返回「调用时刻已结束或正在进行」的条目,不保证覆盖所有历史请求 - 动态插入的 CSS 若未触发重排或未完成加载,可能根本不出现在结果中
- 推荐改用
PerformanceObserver实时订阅,类型设为'resource',并开启buffered: true捕获过往条目
如何精准识别 CSS 动态引入条目
不能只靠 entryType === 'resource',因为所有网络资源都归这一类。关键要结合 initiatorType 和 name 字段:
-
initiatorType === 'link'表示由<link>标签触发(含 JS 动态创建的) -
entry.name包含完整 URL,可用于匹配你预期的 CSS 路径(如/css/animations/fade.css) - 避免误判:排除
initiatorType === 'css'(来自@import)和'script'(JS 内联样式)
示例过滤逻辑:
立即学习“前端免费学习笔记(深入)”;
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (
entry.entryType === 'resource' &&
entry.initiatorType === 'link' &&
entry.name.includes('animations/') &&
entry.name.endsWith('.css')
) {
console.log('CSS 动态加载:', entry.name, '耗时', entry.duration);
}
});
});
observer.observe({ entryTypes: ['resource'], buffered: true });
注意 duration 不等于“样式生效时间”
duration 只反映网络层耗时(从 request start 到 response end),但 CSS 动态插入后还需经历解析、计算、布局、绘制等阶段。若需衡量“视觉可感延迟”,应额外关注:
-
entry.startTime与performance.now()的差值,确认插入时机是否被 JS 阻塞 - 配合
requestIdleCallback或setTimeout(..., 0)观察后续渲染帧,判断是否卡在样式计算 - 在
link.onload回调里打点,比PerformanceEntry更贴近实际就绪时刻
真正容易被忽略的是:动态插入的 <link> 若没加 media="print" 或 disabled 属性,会立即阻塞渲染;而 PerformanceEntry 只记录加载,不体现它对页面 FPS 的拖累。监控脚本里得把 duration 和后续的 longtask 条目关联起来看,才能定位真实瓶颈。



















