第三方脚本性能瓶颈在于DNS、连接或传输阶段而非执行本身,应通过PerformanceObserver实时捕获resource条目,结合URL关键词精准识别,并重点分析domainLookupEnd-domainLookupStart、connectEnd-connectStart、responseEnd-requestStart等字段定位卡点。

直接用 performance.getEntriesByType("resource") 提取所有资源记录,再按 URL 或类型筛选出第三方脚本,就能看到它们真实的 DNS、连接、请求和执行耗时——关键不是“有没有加载”,而是“每一步卡在哪”。
抓取完整资源时间线
第三方脚本是否被缓存、是否跨域、是否触发重定向,都会影响字段完整性。必须在页面 完全加载后(比如 window.addEventListener("load", ...))调用,否则很多条目还没完成,返回空数组或缺失关键字段。
推荐写法:
- 监听
performance.entryType === "resource"的PerformanceObserver,实时捕获每个脚本的加载完成事件 - 避免只依赖首次调用
getEntriesByType,尤其在 SPA 中,动态插入的脚本需单独观察 - 对 CDN 域名(如
https://cdn.segment.com)检查domainLookupStart和connectEnd是否为 0:若为 0,大概率是跨域且服务端没配Timing-Allow-Origin: *,网络阶段数据不可信
精准识别第三方脚本
不能只靠 initiatorType === "script",因为有些埋点 SDK 是通过 fetch 或 XMLHttpRequest 动态拉取并 eval 执行的,不会出现在 script 类型里。
立即学习“前端免费学习笔记(深入)”;
更可靠的方式是结合 URL 特征过滤:
- 用
r.name.includes("google-analytics.com")、r.name.includes("gtm.js")等明确域名关键词匹配 - 排除自家域名:
!r.name.includes(window.location.hostname) - 对聚合类服务(如 Sentry、Bugsnag),可匹配路径关键词:
r.name.includes("/bundle.") || r.name.includes("sentry")
看懂关键耗时字段含义
第三方脚本慢,往往不是代码本身执行慢,而是卡在网络前期环节。重点盯这几个字段(单位毫秒,全基于 performance.timeOrigin):
-
domainLookupEnd - domainLookupStart:DNS 查询耗时。超过 50ms 就该考虑preconnect -
connectEnd - connectStart:TCP + TLS 握手耗时。移动端常达 200–400ms,preconnect能提前完成这步 -
responseEnd - requestStart:实际网络传输(含 header + body)。若远大于首字节时间(responseStart - requestStart),说明 body 下载慢,可能需压缩或换 CDN -
duration:总耗时,但不能单独看——要拆解上面三段,才能定位瓶颈是 DNS、连接还是传输
对比 async/defer 实际效果
加了 async 或 defer 不代表一定快,得验证是否真没阻塞 DOM 构建。
- 查
performance.getEntriesByType("navigation")[0].domContentLoadedEventStart:如果这个时间点明显晚于第三方脚本的responseEnd,说明它仍在阻塞解析(比如用了同步document.write) - 对
async脚本,检查多个同域脚本的执行顺序是否混乱——若业务逻辑有依赖,乱序执行会导致错误,这时必须换defer - 用
performance.getEntriesByName("https://xxx.com/sdk.js")单独取某脚本记录,比对startTime和fetchStart,确认是否真正异步发起请求



















