Performance API可精准捕获动态注入脚本的完整耗时,需在注入前、插入后、加载完成、执行开始、执行结束五节点打标并用measure计算各段,再结合资源类型、模块模式与缓存状态归因分析。
可以直接用 Performance API 精准捕获动态注入脚本(如 document.createElement("script") 或 import())的完整加载与执行耗时,关键不是“有没有开销”,而是“在哪段耗时、由谁主导”。需在创建、插入、加载完成、执行开始、执行结束五个节点打标,再结合资源类型(内联 vs 外链)、模块模式(ESM vs CommonJS)、缓存状态做归因分析。
动态脚本性能不能只看 load 事件,要拆解为可归因的阶段:
performance.mark('script-inject-start'),记录 DOM 操作发起时刻scriptElement.parentNode.insertBefore(...) 后立即打标 performance.mark('script-inserted')
script.onload 或 import() 的 then 回调,打标 'script-loaded'
performance.mark('script-exec-start')(需可控代码);否则用 PerformanceObserver 监听 longtask 或结合 setTimeout(0) 近似定位'script-exec-end'
之后用 performance.measure() 计算各段:例如 measure('inject-to-insert', 'script-inject-start', 'script-inserted')、measure('load-to-exec', 'script-loaded', 'script-exec-start')。
同样一段逻辑,不同注入路径性能差异显著:
performance.getEntriesByType('resource') 查找对应 name,可直接读取 duration(总耗时)、connectStart 等字段定位瓶颈script-exec-start 到 script-exec-end 段会拉长,尤其含大量闭包或正则时import('./mod.js')):首次执行有模块解析开销,且受 type="module" 特性影响(如自动 defer、CSP 限制),script-loaded 到 script-exec-start 可能达几十毫秒transferSize 和 decodedBodySize,若两者接近且 duration 极小,说明命中内存缓存;若 transferSize === 0 但 duration > 0,可能是协商缓存(304)带来的验证延迟对第三方或无法修改的动态脚本(如广告 SDK),无法在内部打标,此时靠 PerformanceObserver 监听 resource 和 longtask 更可靠:
resource 类型,过滤 initiatorType === 'script' 且 name 匹配目标 URL,获取真实加载耗时longtask,当某次任务持续 > 50ms 且紧随脚本 load 事件之后,大概率是该脚本执行导致,可关联时间戳做推测performance.memory 观察 usedJSHeapSize 是否突增,判断是否因脚本引入大量对象引发 GC 延迟高耗时不等于脚本本身差,需交叉验证:
requestAnimationFrame 中密集计算),会导致 script-exec-start 推迟,应检查 Performance 面板中是否存在长任务阻塞XMLHttpRequest 或 localStorage,会直接卡住主线程,这段应出现在 exec-start 到 exec-end 内,而非加载阶段load 耗时低,但 exec 段仍可能波动,需看 V8 code cache 是否命中(DevTools → Application → Cache Storage 中查 script 字段)