Hallo. In den vorherigen Artikeln haben wir über die grundlegenden Dinge der Optimierung gesprochen: eins und zwei . Heute schlage ich vor, mich mit einem Teil der Aufgaben zu befassen, die das Frontend-Architekturteam von hh.ru erledigt.
Ich arbeite im Architektenteam. Wir übertragen nicht nur Dateien von einem Ordner in einen anderen, sondern erledigen auch eine Reihe anderer Dinge:
- Anwendungsleistung
- Infrastruktur: Montage, Tests, Pipelines, Einführung in der Produktion, Entwicklertools (z. B. Babel-Plugins, benutzerdefinierte Eslint-Regeln)
- Design System (UIKit)
- Umstellung auf neue Technologien
Wenn Sie herumgraben, können Sie viele interessante Dinge finden.
Lassen Sie uns deshalb über die Leistung sprechen. Das Frontend-Architektur-Team ist sowohl für das Front-End als auch für das Back-End (SSR) verantwortlich.
Ich schlage vor, die Metriken zu betrachten und zu verstehen, wie wir auf verschiedene Auslöser reagieren. Der Artikel wird in 2 Teile geteilt. Server und Client. Grafiken, Code und Coolstory sind beigefügt.

In diesem Artikel werden wir darüber sprechen, wie wir verfolgen, welche (interessanten) Ergebnisse es gibt. Zur Theorie und warum es besser ist, auf den ersten Artikel zu verweisen.
Klient
— . , , , . - , .
. . , , , . . :
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
})
});
});
:
- body, ( , 1 ). , .
- — ¯(ツ)/¯
, 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>
:

: , .
:
- FMP . , 3 . "" .
- FMP: 10 . .
- , FMP . , .
- , ! , url-. , , 95 :

, . , , .
TTI
TTI . , Page Visibility API, . , TTI longtask " - -", , .
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 ? :
- , requestIdleCallback
- 3d party
, " ". :
()
95" TOP :

? , JS , .
, js , , , .
JS
, hydrate, , :

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

?
- FMP, , . , , SSR .

- , . . .
LongTasks
PerformanceObserver :

:
, , (, 2020!). : ! . .
: , js reflow, event loop 30 .
, . , . , ;)
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. , , , , . - . : , , , \ , .
.