如何单独统计JavaScript脚本的下载耗时与解析执行耗时
脚本分阶段加载耗时统计方案
可以实现分阶段独立统计,核心是利用浏览器原生Performance API的内置记录能力,配合极少量埋点,就能把下载、解析、执行三个阶段的耗时完全拆开,比手动在HTML里打mark的方案精度高很多。
各阶段耗时的统计原理
首先明确普通无async/defer脚本的完整加载时间线,浏览器内核本身会记录每个节点的时间戳,不需要手动提前埋点:
- 浏览器发起脚本资源请求 → 对应资源性能条目的
requestStart字段 - 浏览器接收完脚本的最后一个响应字节 → 对应资源性能条目的
responseEnd字段,这中间的区间就是纯网络下载耗时,完全不包含解析、执行逻辑 - 接收完资源后,浏览器开始解析、编译脚本代码,完成后进入执行阶段
- 脚本所有同步代码执行完毕后,触发script标签的
load事件
各阶段耗时的计算逻辑:
- 纯下载耗时:直接读取对应脚本资源的PerformanceResourceTiming条目,用
responseEnd - requestStart计算即可,这个值是浏览器内核层面记录的网络耗时,不会受预加载扫描器提前发请求、HTML解析阻塞等因素影响,比手动打mark准确。 - 纯同步执行耗时:只需要在目标脚本的最顶部、最底部分别加一行埋点,通过自定义mark统计首尾时间差即可。这个mark对应脚本执行的第一行和最后一行同步代码,统计到的差值完全不包含下载、解析编译的时间。
- 解析编译耗时:用「script标签load事件触发时间 - 资源responseEnd时间」拿到解析+执行的总耗时,再减去上面统计到的纯执行耗时,剩下的就是解析编译的时间。
具体实现代码
HTML侧代码
<script src="something.js" id="core-script" crossorigin></script> <script> const coreScript = document.getElementById('core-script') coreScript.addEventListener('load', () => { // 获取脚本资源的内置性能条目 const resourcePerf = performance.getEntriesByName(coreScript.src)[0] // 计算纯下载耗时 const downloadCost = resourcePerf.responseEnd - resourcePerf.requestStart // 计算解析+执行总耗时 const parseAndExecCost = performance.now() - resourcePerf.responseEnd // 获取脚本内埋点统计的纯执行耗时 const execPerf = performance.getEntriesByName('core-script-exec')[0] const pureExecCost = execPerf.duration // 计算解析编译耗时 const parseCost = parseAndExecCost - pureExecCost // 此处可将各阶段耗时上报 console.log({ downloadCost, parseCost, pureExecCost }) }) </script>
注意:如果脚本是跨域部署的,必须给script标签加
crossorigin属性,同时对应CDN/服务器配置允许跨域访问的响应头,否则ResourceTiming的时间字段会被置0,无法拿到准确数据。
脚本something.js侧埋点
只需要在文件最开头和最结尾加两行代码,不侵入原有业务逻辑:
// something.js 最顶部 performance.mark('core-script-exec-start') /* 原有业务代码 start */ // 你的所有核心JS逻辑 /* 原有业务代码 end */ // something.js 最底部 performance.measure('core-script-exec', 'core-script-exec-start')
原有方案的问题
最初在脚本前打mark的方案统计不准,核心原因有两个:
- 浏览器有预加载扫描器,会在解析HTML的早期提前发现后面的脚本资源并发起请求,手动打的
script-start标记时间晚于实际请求发起时间,算出来的网络耗时偏短 - 标记点到脚本内measure点的区间,混杂了请求排队、网络下载、解析编译、部分执行多个阶段,自然无法拆分独立时长。
内容的提问来源于stack exchange,提问作者Finley Baker
相关产品推荐
相关产品推荐

