直接调用 performance.getEntriesByType('resource') 拿不到 DNS/TCP 等字段,是因为跨域资源默认被浏览器“打码”,仅暴露 startTime 和 duration;必须由第三方服务端响应头显式设置 Timing-Allow-Origin: https://your-site.com 才能解禁完整网络阶段数据。

直接调用 performance.getEntriesByType('resource') 为什么拿不到 DNS/TCP 等字段?
不是 API 失效,而是跨域资源默认被浏览器“打码”——只暴露 startTime 和 duration,其余网络阶段字段(如 domainLookupStart、connectStart)全为 0 或 NaN。
必须由第三方资源的服务端响应头中显式声明:Timing-Allow-Origin: https://your-site.com(开发期可用 *,但生产环境禁止 wildcard)。
- CDN 域名(如
cdn.example.com)没配该头 → 所有网络阶段时间不可信 - 本地开发时用
file://协议加载页面 → 跨域限制更严,连startTime都可能丢失 - 检查方式:在 DevTools 的 Network 面板点开对应请求 → Response Headers → 查找
Timing-Allow-Origin
PerformanceObserver 监听 resource 类型时,buffered: true 为什么不能省?
不加 buffered: true,Observer 只能捕获它启用之后新加载的资源;页面已加载完的 JS、CSS、图片等完全不会触发回调——这会导致你漏掉首屏全部关键资源。
尤其在监控脚本晚于页面注入(比如通过 CDN 异步加载的 RUM SDK)时,buffered: true 是唯一能回溯历史条目的方式。
立即学习“前端免费学习笔记(深入)”;
- 必须在
new PerformanceObserver(...).observe()中显式传入{ buffered: true } - 即使监听器在
window.addEventListener('load', ...)里初始化,也仍需buffered: true,否则无法覆盖 DOMContentLoaded 到 load 之间的资源 - 注意:
buffered: true不影响性能,浏览器已在内存中维护了该 buffer
如何从 entry 中准确识别第三方脚本,而不是只看 initiatorType === 'script'?
initiatorType 只反映资源如何被插入 DOM,不反映来源域名。很多埋点 SDK 是 fetch 后 eval() 或 new Function() 执行的,initiatorType 会是 'fetch' 或 undefined,根本不会归类到 'script'。
可靠做法是结合 URL 特征做白名单/黑名单过滤:
- 匹配已知第三方域名:
entry.name.includes('google-analytics.com') || entry.name.includes('gtm.js') - 排除自身域名:
!entry.name.startsWith(window.location.origin) - 处理聚合服务:
entry.name.includes('/sentry.') || entry.name.includes('bugsnag-js') - 注意协议和端口:
https://api.segment.io/v1/pixel和http://api.segment.io:8080是不同源,需分别判断
为什么 entry.duration 不能用于定位瓶颈?
duration 是 responseEnd - startTime,它跳过了重定向、DNS、TCP、SSL 握手等关键前置环节,纯属“下载完成耗时”,对优化毫无指导意义。
真正要拆解的是四个阶段差值:
- DNS 慢:
entry.domainLookupEnd - entry.domainLookupStart > 100ms → 检查是否缺preconnect或 DNS 缓存失效 - TCP/TLS 卡住:
entry.connectEnd - entry.connectStart > 200ms → iOS 设备易因 OCSP stapling 配置不当触发长延迟 - TTFB 高:
entry.responseStart - entry.requestStart > 500ms → 后端渲染、数据库慢查询、CDN 回源失败 - 下载慢:
entry.responseEnd - entry.responseStart明显偏高 +entry.transferSize接近entry.encodedBodySize→ 未启用 Gzip/Brotli 或带宽受限
零值不是 bug:比如 domainLookupStart === 0 通常表示 DNS 复用或强缓存跳过,反而是健康信号。



















