2

简短的问题

我如何(尽可能可靠地)计算“首屏”内容在视觉上完整的时间,包括应用外部 CSS 和字体以及加载的任何图像。

完整问题

抱歉,这个问题有很多内容,我只是想表明我已经解决了这个问题以及我所处的位置,所以它最终没有成为“我该怎么做”类型的问题并关闭 insta!

我正在尝试确定渲染“首屏”内容所需的所有资源是否已完全下载到客户端浏览器中。

这是纯粹使用浏览器 API(即不使用屏幕截图时间线)模拟SpeedIndex的更大目标的一部分。

我需要收集什么

  1. 出现在页面首屏上方的所有元素。
  2. 确保已加载所有相关资产。
  3. 此外,这些数据正在发送到服务器进行分析,因此我想尽可能地尝试将其保留在一个请求中。

我无法克服的挑战

  1. 必须运行多次获取首屏元素的函数
  2. 确保所有关键资产实际上都加载在非常慢的连接上。
  3. 如果网络流量很高,请确保该功能确实运行,如果网络上从来没有一个安静的 2 秒窗口(可能是由于每秒轮询服务器的老式聊天),它永远不会触发。

首先,这不一定是完美的,它是一个近似值,但我越准确越好!

我目前这样做的方式是使用PerformanceObserver在下载所有资源时列出它们。

第二个我得到一个 2 秒的窗口,没有完成任何请求,我假设关键的 CSS 已经下载并开始查看页面上的图像。

//getRects is the function to get all of the rectangles above the fold.
var rectInterval = setTimeout(getRects, 2500);

 var xx = new PerformanceObserver(function (ll, p) {
        ll.getEntries().forEach(function (en) {
            if (en.name.indexOf(apiEndpoint) == -1) {
                if (en.entryType == "layout-shift") {
                    if (en.entryType == "resource"){
                    //any resources I reset the timer waiting for a quiet time
                    clearTimeout(rectInterval);
                    rectInterval = setTimeout(getRects, 2000);
                }
                
            }
        });
    });
    xx.observe({
        entryTypes: ['largest-contentful-paint', 'longtask', 'resource', 'paint', 'navigation', 'mark', 'measure', 'layout-shift', 'first-input']
    });

getRects我在函数中使用页面上的背景图像获取所有图像和元素的尺寸,然后使用等getBoundingRect()计算它们是否出现在折叠上方。window.innerHeight

这些是下载时要检查的候选对象(以及之前的资源列表等)

var doc = window.document;
var browserWidth = window.innerWidth || doc.documentElement.clientWidth;
var browserHeight = window.innerHeight || doc.documentElement.clientHeight;

function checkRectangle(el){
    var rtrn = false;
    
    
    if (el.getBoundingClientRect) {
       var rect =  el.getBoundingClientRect();
       //check if the bottom is above the top to ensure the element has height, same for width.
       //Then the last 4 checks are to see if the element is in the above the fold viewport.
       
       if (rect.bottom <= rect.top || rect.right <= rect.left || rect.right < 0 || rect.left > browserWidth || rect.bottom < 0 || rect.top > browserHeight) {
           rtrn = false;
       }else{
           rtrn = {};
           rtrn.bot = rect.bottom;
           rtrn.top = rect.top;
           rtrn.left = rect.left;
           rtrn.right = rect.right;
       }
   }
   
   return rtrn;
}



//function to get the rectangles above the fold
function getRects(){
    
    var rects = [];
     var elements = doc.getElementsByTagName('*');
    var re = /url\(.*(http.*)\)/ig;
    for (var i = 0; i < elements.length; i++) {
        var el = elements[i];
        var style = getComputedStyle(el);
        
        if(el.tagName == "IMG"){
            var rect = checkRectangle(el);
            if(rect){
                //The URL is stored here for later processing where I match performance timings to the element, it is not relevant other than to show why I convert the `getBoundingClientRect()` to a simple object. 
                rect.url = el.src;
                rects.push(rect);
            }
        }
        //I also need to check for background images set in either CSS or with inline styles.
        if (style['background-image']) {
             var rect = checkRectangle(el);
             if(rect){
                var matches = re.exec(style['background-image']);
                if (matches && matches.length > 1){
                    rect.url = matches[1].replace('"', '');
                    rects.push(rect);
                }
                
             }
            
        }
        
    }

这一点很好(尽管任何缩小搜索范围的提示,所以我不会遍历所有内容都会很棒),但我的问题出在加载缓慢的网站上。如果请求之间的间隔超过 2 秒(这可能发生在连接特别慢或服务器距离用户很远的情况下),那么我将无法获得完整的数据。

我的解决方法是监视进一步的网络请求(再次等待请求之间的 2 秒延迟)并重新运行该函数以收集上述折叠内容。如果站点在滚动时使用延迟加载,这显然不能很好地工作,因为请求可以在整个页面生命周期中不断触发。

由于在一个非常重的页面上收集元素的维度可能会占用大量 CPU,再加上需要将此数据发送到服务器进行分析,因此我试图找到一种更可靠的方法来确保加载所有关键内容。或者一种只开火getRect一次但确保所有初始加载完成的方法。

假设如果有效负载足够小(例如小于 1kb),任何数据操作都可以稍后在服务器上完成

我考虑过的事情

  1. 寻找任何<links><scripts>并检查它们是否已加载。问题来自于动态添加的链接以及外部资源(即链接在另一个样式表中的样式表)。这可能会更强大,但会变得非常复杂。
  2. 设置检查之间的时间(我正在等待的安静时间)更长。这显然会使流量大的网站问题变得更糟,因为“安静时间”可能永远不会发生。
  3. 用于MutationObserver监视页面并再次等待安静时间。但是,据我所知,如果页面具有任何交互性,这会更频繁地被触发?
  4. 我知道我的方法会过度报告正确内联其 CSS 的网站,这不是问题。

作为解决这个难题的一种方式,我是否走在正确的轨道上,或者是否有一些我可以根据window.performance数据(或类似的 API)使用的简单公式让我说“所有折叠元素都已加载和渲染”。

我希望这很清楚,但任何问题都只是问,因为我知道这个问题有很多可以简单地回答“我如何检查所有关键资源是否已加载”。

4

0 回答 0