Rendimiento front-end como arte contemporáneo: gráficos, código, coolstory

Hola. En artículos anteriores, hablamos sobre los aspectos básicos de la optimización: uno y dos . Hoy propongo sumergirnos en una parte de las tareas que está realizando el equipo de arquitectura frontend de hh.ru.



Trabajo en el equipo de arquitectura. No solo transferimos archivos de una carpeta a otra, sino que también hacemos muchas otras cosas:



  • Rendimiento de la aplicación
  • Infraestructura: ensamblaje, pruebas, canalizaciones, implementación en producción, herramientas de desarrollo (por ejemplo, complementos de babel, reglas de eslint personalizadas)
  • Sistema de diseño (UIKit)
  • Pasar a nuevas tecnologías


Si investigas, puedes encontrar muchas cosas interesantes.



Por tanto, hablemos de rendimiento. El equipo de arquitectura de frontend es responsable tanto del front end como del back end (SSR).



Sugiero mirar las métricas y comprender cómo respondemos a los diferentes factores desencadenantes. El artículo se dividirá en 2 partes. Servidor y cliente. Se adjuntan gráficos, código y coolstory.





En este artículo hablaremos sobre cómo rastreamos, qué resultados (interesantes) hay. Para la teoría y por qué es mejor consultar el primer artículo.



Cliente



— . , , , . - , .



. . , , , . . :



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