こんにちは。前回の記事では、我々は、最適化の基本的な事柄について話しました:1と2。今日は、hh.ruのフロントエンドアーキテクチャチームが行っているタスクの一部に飛び込むことを提案します。
私は建築チームで働いています。あるフォルダから別のフォルダにファイルを転送するだけでなく、他にもたくさんのことを行います。
- アプリケーションのパフォーマンス
- インフラストラクチャ:アセンブリ、テスト、パイプライン、本番環境への展開、開発者ツール(たとえば、babelプラグイン、カスタムeslintルール)
- デザインシステム(UIKit)
- 新しいテクノロジーへの移行
掘り下げると、面白いものがたくさん見つかります。
したがって、パフォーマンスについて話しましょう。フロントエンドアーキテクチャチームは、フロントエンドとバックエンド(SSR)の両方を担当します。
指標を見て、さまざまなトリガーにどのように対応するかを理解することをお勧めします。記事は2つの部分に分かれます。サーバーとクライアント。グラフ、コード、クールストーリーが添付されています。
この記事では、追跡方法、そこにある(興味深い)結果について説明します。理論と、最初の記事を参照する方がよい理由については。
クライアント
— . , , , . - , .
. . , , , . . :
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. , , , , . - . : , , , \ , .
.