Front-end performance as contemporary art: graphics, code, coolstory

Hello. In the previous articles, we talked about the basic things of optimization: one and two . Today I propose to dive into one part of the tasks that the frontend architecture team at hh.ru is doing.



I work in the architecture team. We not only transfer files from one folder to another, but also do a bunch of other things:



  • Application performance
  • Infrastructure: assembly, tests, pipelines, rolling out to production, developer tools (for example, babel plugins, custom eslint rules)
  • Design System (UIKit)
  • Moving to new technologies


If you dig around, you can find a lot of interesting things.



Therefore, let's talk about performance. The frontend architecture team is responsible for both the front end and the back end (SSR).



I suggest looking at the metrics and understanding how we respond to different triggers. The article will be split into 2 parts. Server and client. Graphs, code and coolstory are attached.





In this article we will talk about how we track, what (interesting) results are there. For theory and how much it is better to refer to the first article.



Client



— . , , , . - , .



. . , , , . . :



FMP (first meaningful paint)





FMP : . — . TOP . 95 . .



, :



FMP :



  • hh.ru — . , , , — , .
  • — . — .


FMP — . FMP? .



, FMP - :



requestAnimationFrame(function() { 
  //       renderTree  
  var renderTreeFormed = performance.now();
  requestAnimationFrame(function() {
    //    
    var fmp = performance.now();
    //      
    window.globalVars.performance.fmp.push({
      renderTreeFormed: renderTreeFormed,
      fmp: fmp
    })
  });
});


:



  1. body, ( , 1 ). , .
  2. — ¯(ツ)/¯




, raf setTimeout\interval . .



, - . PageVisibility API:



window.globalVars = window.globalVars || {};
window.globalVars.performance = window.globalVars.performance || {};
// ,       
window.globalVars.performance.pageWasActive = document.visibilityState === "visible";

document.addEventListener("visibilitychange", function(e) {
    //  -  — 
    if (document.visibilityState !== "visible") {
        window.globalVars.performance.pageWasActive = false;
    }
});


FMP:



requestAnimationFrame(function() { 
  //       renderTree  
  var renderTreeFormed = performance.now();
  requestAnimationFrame(function() {
    //    
    var fmp = performance.now();
    //      , 
    //   ,      
    if (window.globalVars.performance.pageWasActive) {
        window.globalVars.performance.fmp.push({
          renderTreeFormed: renderTreeFormed,
          fmp: fmp
        });
        }
  });
});


, . .

: — , " " , . .



, , ? , , renderTree () , , , .



. ( fmp_menu ):



<script>window.performance.mark('fmp_body')</script>


:



: , .



:



  1. FMP . , 3 . "" .
  2. FMP: 10 . .
  3. , FMP . , .
  4. , ! , url-. , , 95 :



, . , , .



TTI



, TTI



TTI . , Page Visibility API, . , TTI longtask " - -", , .



TTI
function timeToInteractive() {
    //   TTI
    const LONG_TASK_TIME = 2000;
    //    TTI,    
    const MAX_LONG_TASK_TIME = 30000;

    const metrics = {
        report: 'TTI_WITH_VISIBILITY_API',
        mobile: Supports.mobile(),
    };

    if ('PerformanceObserver' in window && 'PerformanceLongTaskTiming' in window) {
        let timeoutIdCheckTTI;
        const longTask = [];
        const observer = new window.PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                longTask.push(Math.round(entry.startTime + entry.duration));
            }
        });

        observer.observe({ entryTypes: ['longtask'] });

        const checkTTI = () => {
            if (longTask.length === 0 && performance.now() > MAX_LONG_TASK_TIME) {
                clearTimeout(timeoutIdCheckTTI);
            }

            const eventTime = longTask[longTask.length - 1];

            if (eventTime && performance.now() - eventTime >= LONG_TASK_TIME) {
                if (window.globalVars?.performance?.pageWasActive) {
                    StatsSender.sendMetrics({ ...metrics, tti: eventTime });
                }
            } else {
                timeoutIdCheckTTI = setTimeout(checkTTI, LONG_TASK_TIME);
            }
        };

        checkTTI();
    }
}

export default timeToInteractive;


TTI (95", TOP ):



: TTI ? :



  1. , requestIdleCallback
  2. 3d party


, " ". :



()



95" TOP :



? , JS , .

, js , , , .



JS





, hydrate, , :





, :



  1. , , ( \ , - )
  2. — . , :



?



  1. FMP, , . , , SSR .

  2. , . . .


LongTasks



PerformanceObserver :



:



, , (, 2020!). : ! . .



: , js reflow, event loop 30 .



, . , . , ;)



2 :



  1. ,
  2. Longtasks. — .


?



, . -, , rate + , , FID. .



, .





. — . , .



? , .



, , CPU, — . SSR. :







http



— , TOP . 95 . , 12:10 12:40. " ", 400 , , " ". :



c





, parse time.



CPU. :





, . 1 . .



: . 12 , . 150, 400 .



. . , , slack :





.



render parse time :





, :





,



TypeError: Cannot read property 'map' of undefined
    at Social (at path/to/module)


, .



, , , :





, parse time :





. . :





SSR as a service. BFF, node.js , . BFF .



, , , : BFF , node.js. — BFF . , .



BFF . \ .





:



. .



, .



, . . FMP. , , , , . - . : , , , \ , .



.




All Articles