La performance front-end comme art contemporain: graphisme, code, coolstory

Bonjour. Dans les articles précédents, nous avons parlé des éléments de base de l'optimisation: un et deux . Aujourd'hui, je propose de plonger dans une partie des tâches que fait l'équipe d'architecture frontend de hh.ru.



Je travaille dans l'équipe d'architecture. Nous transférons non seulement des fichiers d'un dossier à un autre, mais nous faisons également un tas d'autres choses:



  • Performances des applications
  • Infrastructure: assemblage, tests, pipelines, déploiement en production, outils de développement (par exemple, plugins babel, règles eslint personnalisées)
  • Système de conception (UIKit)
  • Passer aux nouvelles technologies


Si vous creusez, vous pouvez trouver beaucoup de choses intéressantes.



Par conséquent, parlons de performance. L'équipe d'architecture frontend est responsable à la fois du frontend et du backend (SSR).



Je suggère d'examiner les métriques et de comprendre comment nous répondons aux différents déclencheurs. L'article sera divisé en 2 parties. Serveur et client. Les graphiques, le code et la coolstory sont joints.





Dans cet article, nous parlerons de la façon dont nous suivons, des résultats (intéressants). Pour la théorie et pourquoi il est préférable de se référer au premier 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