Halo. Pada artikel sebelumnya, kita telah membahas tentang dasar-dasar optimasi: satu dan dua . Hari ini saya mengusulkan untuk menyelami salah satu bagian dari tugas yang dilakukan oleh tim arsitektur frontend di hh.ru.
Saya bekerja di tim arsitektur. Kami tidak hanya mentransfer file dari satu folder ke folder lain, tetapi juga melakukan banyak hal lain:
- Performa aplikasi
- Infrastruktur: perakitan, pengujian, pipeline, peluncuran ke produksi, alat pengembang (misalnya, plugin babel, aturan eslint khusus)
- Sistem Perancangan (UIKit)
- Pindah ke teknologi baru
Jika Anda menggali, Anda dapat menemukan banyak hal menarik.
Oleh karena itu, mari kita bicara tentang kinerja. Tim arsitektur frontend bertanggung jawab atas front end dan back end (SSR).
Saya sarankan untuk melihat metrik dan memahami bagaimana kita menanggapi pemicu yang berbeda. Artikel akan dibagi menjadi 2 bagian. Server dan klien. Grafik, kode dan cerita keren dilampirkan.

Pada artikel ini kita akan berbicara tentang bagaimana kita melacak, hasil (menarik) apa yang ada. Untuk teori dan seberapa baik lebih baik mengacu pada artikel pertama.
Klien
— . , , , . - , .
. . , , , . . :
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. , , , , . - . : , , , \ , .
.