[Dev] Teact: Add debug overlay
This commit is contained in:
parent
07e7253b02
commit
56dbad1ef9
@ -1,7 +1,7 @@
|
||||
import { useCallback, useState } from '../lib/teact/teact';
|
||||
|
||||
const useFlag = (initial = false): [boolean, NoneToVoidFunction, NoneToVoidFunction] => {
|
||||
const [value, setValue] = useState(initial);
|
||||
const useFlag = (initial = false, debugKey?: string): [boolean, NoneToVoidFunction, NoneToVoidFunction] => {
|
||||
const [value, setValue] = useState(initial, debugKey);
|
||||
|
||||
const setTrue = useCallback(() => {
|
||||
setValue(true);
|
||||
|
||||
@ -6,13 +6,12 @@ import {
|
||||
import { orderBy } from '../../util/iteratees';
|
||||
import { getUnequalProps } from '../../util/arePropsShallowEqual';
|
||||
import { handleError } from '../../util/handleError';
|
||||
import { incrementOverlayCounter } from '../../util/debugOverlay';
|
||||
|
||||
export type Props = AnyLiteral;
|
||||
export type FC<P extends Props = any> = (props: P) => any;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export type FC_withDebug = FC & {
|
||||
DEBUG_contentComponentName?: string;
|
||||
};
|
||||
export type FC_withDebug = FC & { DEBUG_contentComponentName?: string };
|
||||
|
||||
export enum VirtualElementTypesEnum {
|
||||
Empty,
|
||||
@ -339,6 +338,11 @@ export function renderComponent(componentInstance: ComponentInstance) {
|
||||
}
|
||||
DEBUG_components[componentName].renderTimes.push(duration);
|
||||
DEBUG_components[componentName].renderCount++;
|
||||
|
||||
if (DEBUG_MORE) {
|
||||
incrementOverlayCounter(`${componentName} renders`);
|
||||
incrementOverlayCounter(`${componentName} duration`, duration);
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
handleError(err);
|
||||
|
||||
109
src/util/debugOverlay.ts
Normal file
109
src/util/debugOverlay.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { throttle } from './schedulers';
|
||||
|
||||
const KEYS_TO_IGNORE = new Set([
|
||||
'TeactMemoWrapper renders',
|
||||
'TeactNContainer renders',
|
||||
'Button renders',
|
||||
]);
|
||||
const MIN_RENDERS_TO_SHOW = 5;
|
||||
const MIN_DURATION_TO_SHOW = 2;
|
||||
const BG_GREEN = ' style="background: lightgreen"';
|
||||
|
||||
let counters: Record<string, {
|
||||
value: number;
|
||||
lastUpdateAt: number;
|
||||
}> = {};
|
||||
|
||||
const renderCountersThrottled = throttle(renderCounters, 500, false);
|
||||
|
||||
let loggerEl: HTMLDivElement;
|
||||
|
||||
export function debugToOverlay(text: string) {
|
||||
if (!loggerEl) {
|
||||
setupOverlay();
|
||||
}
|
||||
|
||||
const date = new Date();
|
||||
const dateFormatted = `${date.toLocaleTimeString()}.${date.getMilliseconds()}`;
|
||||
const wasAtBottom = loggerEl.scrollTop + 10 >= loggerEl.scrollHeight - loggerEl.offsetHeight;
|
||||
|
||||
loggerEl.innerHTML += `${dateFormatted}: ${text}<br/>`;
|
||||
|
||||
if (wasAtBottom) {
|
||||
loggerEl.scrollTop = loggerEl.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
export function incrementOverlayCounter(key: string, value = 1) {
|
||||
const now = Date.now();
|
||||
if (!counters[key]) {
|
||||
counters[key] = { value, lastUpdateAt: now };
|
||||
} else {
|
||||
counters[key].value += value;
|
||||
counters[key].lastUpdateAt = now;
|
||||
}
|
||||
|
||||
renderCountersThrottled();
|
||||
}
|
||||
|
||||
export function renderCounters() {
|
||||
if (!loggerEl) {
|
||||
setupOverlay();
|
||||
}
|
||||
|
||||
const halfSecondAgo = Date.now() - 500;
|
||||
const [maxRenders, maxDuration] = Object.entries(counters).reduce((acc, [key, { value }]) => {
|
||||
if (KEYS_TO_IGNORE.has(key)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (key.includes('renders') && value > acc[0]) {
|
||||
acc[0] = value;
|
||||
}
|
||||
|
||||
if (key.includes('duration') && value > acc[1]) {
|
||||
acc[1] = value;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [0, 0]);
|
||||
|
||||
loggerEl.innerHTML = Object
|
||||
.entries(counters)
|
||||
.filter(([key, { value }]) => (
|
||||
(!KEYS_TO_IGNORE.has(key)) && (
|
||||
(key.includes('renders') && value > MIN_RENDERS_TO_SHOW)
|
||||
|| (key.includes('duration') && value > MIN_DURATION_TO_SHOW)
|
||||
)
|
||||
))
|
||||
.sort((a, b) => (
|
||||
b[1].lastUpdateAt - a[1].lastUpdateAt
|
||||
))
|
||||
.map(([key, { value, lastUpdateAt }]) => ([
|
||||
`<div style="background: #ff0000${factorToHex(value / (key.includes('renders') ? maxRenders : maxDuration))}">`,
|
||||
` <span${lastUpdateAt > halfSecondAgo ? BG_GREEN : ''}>${key}: ${Math.round(value)}</span>`,
|
||||
'</div>',
|
||||
].join('\n')))
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function setupOverlay() {
|
||||
loggerEl = document.createElement('div');
|
||||
loggerEl.style.cssText = 'position: absolute; left: 0; bottom: 25px; z-index: 9998; width: 260px; height: 200px;'
|
||||
+ ' border: 1px solid #555; background: rgba(255, 255, 255, 0.9); overflow: auto; font-size: 10px;';
|
||||
document.body.appendChild(loggerEl);
|
||||
|
||||
const clearEl = document.createElement('a');
|
||||
clearEl.style.cssText = 'position: absolute; left: 222px; bottom: 198px; z-index: 9999; font-size: 20px; '
|
||||
+ 'cursor: pointer;';
|
||||
clearEl.innerText = '🔄';
|
||||
clearEl.addEventListener('click', () => {
|
||||
counters = {};
|
||||
renderCountersThrottled();
|
||||
});
|
||||
document.body.appendChild(clearEl);
|
||||
}
|
||||
|
||||
function factorToHex(factor: number) {
|
||||
return Math.round(255 * factor).toString(16).padStart(2, '0');
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user