你好。在前面的文章中,我们讨论了优化的基本内容:一和二。今天,我建议深入研究hh.ru前端架构团队正在执行的任务的一部分。
我在建筑团队工作。我们不仅将文件从一个文件夹传输到另一个文件夹,而且还执行其他许多操作:
- 应用性能
- 基础架构:组装,测试,管道,正式投入生产,开发人员工具(例如babel插件,自定义eslint规则)
- 设计系统(UIKit)
- 转向新技术
如果您四处挖掘,会发现很多有趣的东西。
因此,让我们谈谈性能。前端体系结构团队负责前端和后端(SSR)。
我建议您查看指标并了解我们如何应对不同的触发因素。本文将分为两部分。服务器和客户端。附有图形,代码和coolstory。
在本文中,我们将讨论我们如何跟踪以及有哪些(有趣的)结果。对于理论以及为什么最好参考第一篇文章。
客户
— . , , , . - , .
. . , , , . . :
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. , , , , . - . : , , , \ , .
.