我如何使用 Puppeteer 计算页面完全加载?
How I can calculate page fully load with Pupppeteer?
我正在尝试使用 Node 中的 puppeteer 以秒为单位获取页面完全加载时间,为此我对 API 和其他问题进行了一些研究并创建了以下代码:
/* First Configuration */
puppeteer.launch({
defaultViewport: { width: 1600, height: 800 }
}).then(async browser => {
const page = await browser.newPage();
await page.setCacheEnabled(false);
await page.goto('https://whosebug.com', {waitUntil: 'networkidle0'});
/* Get Page Metrics */
const perf = await page.metrics();
console.log(JSON.stringify(perf));
/* Get Page Evaluate */
const timing = await page.evaluate(() => {
const result = {};
for (const key of Object.keys(window.performance.timing.__proto__))
result[key] = window.performance.timing[key];
return result;
});
console.log(JSON.stringify(timing));
/* Show Results on Browser Close */
await browser.close().then(() => {
var fullyLoadEvaluate = (timing.loadEventEnd - timing.navigationStart);
console.log('Fully Load Time (Page Evaluate): ' + fullyLoadEvaluate);
var fullyLoadMetrics = (perf.LayoutDuration + perf.RecalcStyleDuration + perf.ScriptDuration + perf.TaskDuration);
console.log('Fully Load Time (Page Metrics): ' + fullyLoadMetrics);
/* Send Response to Server */
res.send('Check The Console');
});
});
基本上我使用两个代码来return指标,其中一个是page.metrics()
即return以下数据:
{"Timestamp":961736.600171,"Documents":8,"Frames":4,"JSEventListeners":375,"Nodes":8654,"LayoutCount":27,"RecalcStyleCount":31,"LayoutDuration":0.705517,"RecalcStyleDuration":0.144379,"ScriptDuration":0.527385,"TaskDuration":1.812213,"JSHeapUsedSize":11082496,"JSHeapTotalSize":20344832}
和最后一个page.evaluate()
、return以下:
{"navigationStart":1556722407938,"unloadEventStart":0,"unloadEventEnd":0,"redirectStart":0,"redirectEnd":0,"fetchStart":1556722407938,"domainLookupStart":1556722408247,"domainLookupEnd":1556722408548,"connectStart":1556722408548,"connectEnd":1556722408737,"secureConnectionStart":1556722408574,"requestStart":1556722408738,"responseStart":1556722408940,"responseEnd":1556722409087,"domLoading":1556722408957,"domInteractive":1556722409995,"domContentLoadedEventStart":1556722409995,"domContentLoadedEventEnd":1556722410190,"domComplete":1556722412584,"loadEventStart":1556722412584,"loadEventEnd":1556722412589,"toJSON":{}}
在我的示例中,我正在测试站点 https://whosebug.com. Like webpagetest.org and getmetrix.com,我试图获得 页面完全加载时间 。
我知道这种值不一致,但我想知道我计算的值是否正确,这两个结果中哪个更正确? Fully Load Time (Page Evaluate)
或 Fully Load Time (Page Metrics)
?
您可以使用page.metrics()
来比较两个时间点(例如page.goto
之前和之后)。从 performance
API 中读取数据的 page.evaluate
方法也是一个不错的选择。正如我已经在评论中指出的那样,没有定义什么应该被视为 "full page load"。两种方法都有效。
更复杂
人们可能认为要加载的页面有很多:
DOMContentLoaded
事件触发
Load
事件触发
- 从导航开始到文档中嵌入的所有资源(如加载图像)所花费的时间
- 从导航开始到加载所有资源所花费的时间
- 没有更多正在进行的网络请求之前的时间。
- ...
您还必须考虑是否希望网络相关阶段(如 DNS)成为测量的一部分。这是一个示例请求(使用 Chrome DevTools Network 选项卡生成)显示单个请求可能有多复杂:
还有一个 document 解释每个阶段。
简单的方法
测量加载时间最简单的方法就是在导航开始时开始测量,并在页面加载后停止测量。可以这样做:
const t1 = Date.now();
await page.goto('https://example.com');
const diff1 = Date.now() - t1;
console.log(`Time: ${diff1}ms`);
请注意,还有其他 API(page.metrics
、process.hrtime
, perf_hooks
)以获得更精确的时间戳。
您还可以将选项传递给 page.goto
函数,以将承诺的解析更改为如下内容(引用自文档):
Consider navigation to be finished when there are no more than 0 network connections for at least 500ms
为此,您必须使用设置 networkidle0
:
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
上面链接的文档中还有其他事件可供您使用。
更复杂:使用性能 API
要获得更精确的结果,您可以像这样使用 Performance API as you already did in your code. Instead of going through the prototype of window.performance
you can also use the functions performance.getEntries()
or performance.toJSON()
:
const perfData = await page.evaluate(() =>
JSON.stringify(performance.toJSON(), null, 2)
);
这样,您将获得如下所示的数据:
{
"timeOrigin": 1556727036740.113,
"timing": {
"navigationStart": 1556727036740,
"unloadEventStart": 0,
"unloadEventEnd": 0,
"redirectStart": 0,
"redirectEnd": 0,
"fetchStart": 1556727037227,
"domainLookupStart": 1556727037230,
"domainLookupEnd": 1556727037280,
"connectStart": 1556727037280,
"connectEnd": 1556727037348,
"secureConnectionStart": 1556727037295,
"requestStart": 1556727037349,
"responseStart": 1556727037548,
"responseEnd": 1556727037805,
"domLoading": 1556727037566,
"domInteractive": 1556727038555,
"domContentLoadedEventStart": 1556727038555,
"domContentLoadedEventEnd": 1556727038570,
"domComplete": 1556727039073,
"loadEventStart": 1556727039073,
"loadEventEnd": 1556727039085
},
"navigation": {
"type": 0,
"redirectCount": 0
}
}
因此,如果您想知道从 navigationStart
到 loadEventStart
花了多长时间,您可以从另一个值中减去一个值(例如 1556727039073
- 1556727036740
= 2333
毫秒)。
那么选择哪一个?
这取决于您的决定。通常,使用 Load
事件作为起点是个好主意。等待所有请求完成可能实际上永远不会发生,因为后台不断加载资源。如果您不想使用加载事件,则将 networkidle2
用作 waitUntil
option 可能是一种替代方法。
不过,最终还是要看您的用例要使用哪个指标。
我正在尝试使用 Node 中的 puppeteer 以秒为单位获取页面完全加载时间,为此我对 API 和其他问题进行了一些研究并创建了以下代码:
/* First Configuration */
puppeteer.launch({
defaultViewport: { width: 1600, height: 800 }
}).then(async browser => {
const page = await browser.newPage();
await page.setCacheEnabled(false);
await page.goto('https://whosebug.com', {waitUntil: 'networkidle0'});
/* Get Page Metrics */
const perf = await page.metrics();
console.log(JSON.stringify(perf));
/* Get Page Evaluate */
const timing = await page.evaluate(() => {
const result = {};
for (const key of Object.keys(window.performance.timing.__proto__))
result[key] = window.performance.timing[key];
return result;
});
console.log(JSON.stringify(timing));
/* Show Results on Browser Close */
await browser.close().then(() => {
var fullyLoadEvaluate = (timing.loadEventEnd - timing.navigationStart);
console.log('Fully Load Time (Page Evaluate): ' + fullyLoadEvaluate);
var fullyLoadMetrics = (perf.LayoutDuration + perf.RecalcStyleDuration + perf.ScriptDuration + perf.TaskDuration);
console.log('Fully Load Time (Page Metrics): ' + fullyLoadMetrics);
/* Send Response to Server */
res.send('Check The Console');
});
});
基本上我使用两个代码来return指标,其中一个是page.metrics()
即return以下数据:
{"Timestamp":961736.600171,"Documents":8,"Frames":4,"JSEventListeners":375,"Nodes":8654,"LayoutCount":27,"RecalcStyleCount":31,"LayoutDuration":0.705517,"RecalcStyleDuration":0.144379,"ScriptDuration":0.527385,"TaskDuration":1.812213,"JSHeapUsedSize":11082496,"JSHeapTotalSize":20344832}
和最后一个page.evaluate()
、return以下:
{"navigationStart":1556722407938,"unloadEventStart":0,"unloadEventEnd":0,"redirectStart":0,"redirectEnd":0,"fetchStart":1556722407938,"domainLookupStart":1556722408247,"domainLookupEnd":1556722408548,"connectStart":1556722408548,"connectEnd":1556722408737,"secureConnectionStart":1556722408574,"requestStart":1556722408738,"responseStart":1556722408940,"responseEnd":1556722409087,"domLoading":1556722408957,"domInteractive":1556722409995,"domContentLoadedEventStart":1556722409995,"domContentLoadedEventEnd":1556722410190,"domComplete":1556722412584,"loadEventStart":1556722412584,"loadEventEnd":1556722412589,"toJSON":{}}
在我的示例中,我正在测试站点 https://whosebug.com. Like webpagetest.org and getmetrix.com,我试图获得 页面完全加载时间 。
我知道这种值不一致,但我想知道我计算的值是否正确,这两个结果中哪个更正确? Fully Load Time (Page Evaluate)
或 Fully Load Time (Page Metrics)
?
您可以使用page.metrics()
来比较两个时间点(例如page.goto
之前和之后)。从 performance
API 中读取数据的 page.evaluate
方法也是一个不错的选择。正如我已经在评论中指出的那样,没有定义什么应该被视为 "full page load"。两种方法都有效。
更复杂
人们可能认为要加载的页面有很多:
DOMContentLoaded
事件触发Load
事件触发- 从导航开始到文档中嵌入的所有资源(如加载图像)所花费的时间
- 从导航开始到加载所有资源所花费的时间
- 没有更多正在进行的网络请求之前的时间。
- ...
您还必须考虑是否希望网络相关阶段(如 DNS)成为测量的一部分。这是一个示例请求(使用 Chrome DevTools Network 选项卡生成)显示单个请求可能有多复杂:
还有一个 document 解释每个阶段。
简单的方法
测量加载时间最简单的方法就是在导航开始时开始测量,并在页面加载后停止测量。可以这样做:
const t1 = Date.now();
await page.goto('https://example.com');
const diff1 = Date.now() - t1;
console.log(`Time: ${diff1}ms`);
请注意,还有其他 API(page.metrics
、process.hrtime
, perf_hooks
)以获得更精确的时间戳。
您还可以将选项传递给 page.goto
函数,以将承诺的解析更改为如下内容(引用自文档):
Consider navigation to be finished when there are no more than 0 network connections for at least 500ms
为此,您必须使用设置 networkidle0
:
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
上面链接的文档中还有其他事件可供您使用。
更复杂:使用性能 API
要获得更精确的结果,您可以像这样使用 Performance API as you already did in your code. Instead of going through the prototype of window.performance
you can also use the functions performance.getEntries()
or performance.toJSON()
:
const perfData = await page.evaluate(() =>
JSON.stringify(performance.toJSON(), null, 2)
);
这样,您将获得如下所示的数据:
{
"timeOrigin": 1556727036740.113,
"timing": {
"navigationStart": 1556727036740,
"unloadEventStart": 0,
"unloadEventEnd": 0,
"redirectStart": 0,
"redirectEnd": 0,
"fetchStart": 1556727037227,
"domainLookupStart": 1556727037230,
"domainLookupEnd": 1556727037280,
"connectStart": 1556727037280,
"connectEnd": 1556727037348,
"secureConnectionStart": 1556727037295,
"requestStart": 1556727037349,
"responseStart": 1556727037548,
"responseEnd": 1556727037805,
"domLoading": 1556727037566,
"domInteractive": 1556727038555,
"domContentLoadedEventStart": 1556727038555,
"domContentLoadedEventEnd": 1556727038570,
"domComplete": 1556727039073,
"loadEventStart": 1556727039073,
"loadEventEnd": 1556727039085
},
"navigation": {
"type": 0,
"redirectCount": 0
}
}
因此,如果您想知道从 navigationStart
到 loadEventStart
花了多长时间,您可以从另一个值中减去一个值(例如 1556727039073
- 1556727036740
= 2333
毫秒)。
那么选择哪一个?
这取决于您的决定。通常,使用 Load
事件作为起点是个好主意。等待所有请求完成可能实际上永远不会发生,因为后台不断加载资源。如果您不想使用加载事件,则将 networkidle2
用作 waitUntil
option 可能是一种替代方法。
不过,最终还是要看您的用例要使用哪个指标。