如何使用 Puppeteer 计算页面完全加载?

Sud*_*Sur 3 node.js puppeteer

我正在尝试使用 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://stackoverflow.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');
        });

      });
Run Code Online (Sandbox Code Playgroud)

基本上我使用两个代码来返回指标,其中之一是page.metrics()返回以下数据:

{"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}
Run Code Online (Sandbox Code Playgroud)

最后一个page.evaluate(),返回以下内容:

{"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":{}}
Run Code Online (Sandbox Code Playgroud)

在我的示例中,我正在测试站点https://stackoverflow.com。像pagestest.orggetmetrix.com 一样,我正在尝试获取页面完全加载时间

我知道这种值是不一致的,但我想知道这些值是否正确?我计算的结果是否正确,两个结果中哪个更正确?Fully Load Time (Page Evaluate)或者Fully Load Time (Page Metrics)

Tho*_*orf 7

您可以page.metrics()用来比较两个时间点(例如 before 和 after page.goto)。page.evaluateperformanceAPI读取数据的方法也是一个不错的选择。正如我在评论中已经指出的那样,并没有定义什么应该被视为“整页加载”。这两种方法都是有效的。

它更复杂

人们可能会认为要加载的页面有很多:

  • DOMContentLoaded 事件触发
  • Load 事件触发
  • 从导航开始到所有资源嵌入文档(如加载图像)所需的时间
  • 从导航开始到所有资源加载所需的时间
  • 直到没有更多正在进行的网络请求的时间。
  • ...

您还必须考虑是否希望网络相关阶段(如 DNS)成为测量的一部分。这是一个示例请求(使用 Chrome DevTools Network 选项卡生成)显示单个请求可能有多复杂:

Chrome DevTools 网络计时

还有一份文件解释了每个阶段。

简单的方法

测量加载时间的最简单方法是在导航开始时开始测量,并在页面加载后停止测量。这可以像这样完成:

const t1 = Date.now();
await page.goto('https://example.com');
const diff1 = Date.now() - t1;
console.log(`Time: ${diff1}ms`);
Run Code Online (Sandbox Code Playgroud)

请注意,还有其他 API ( page.metrics, process.hrtime, perf_hooks) 可以获取更精确的时间戳。

您还可以将选项传递给page.goto函数以将承诺的解析更改为这样的内容(引自文档):

当至少 500ms 的网络连接数不超过 0 时,认为导航完成

为此,您必须使用设置networkidle0

await page.goto('https://example.com', { waitUntil: 'networkidle0' });
Run Code Online (Sandbox Code Playgroud)

您可以使用上面链接的文档中的其他事件。

更复杂:使用 Performance API

要获得更精确的结果,您可以像在代码中一样使用Performance APIwindow.performance您还可以使用函数performance.getEntries()performance.toJSON()像这样,而不是通过原型:

const perfData = await page.evaluate(() =>
    JSON.stringify(performance.toJSON(), null, 2)
);
Run Code Online (Sandbox Code Playgroud)

这样,您将获得如下所示的数据:

{
  "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
  }
}
Run Code Online (Sandbox Code Playgroud)

所以,如果你想知道它花了多长时间navigationStartloadEventStart你减去从其他的一个一个值(例如1556727039073- 1556727036740=2333毫秒)。

那么该拿哪一个呢?

这取决于你的决定。一般来说,以Load事件为起点是个好主意。等待所有请求完成实际上可能永远不会发生,因为后台不断加载资源。如果您不想使用 load 事件,则使用networkidle2aswaitUntil 选项可能是另一种选择。

然而,最终取决于您的用例使用哪个指标。