[Perf] Teact: Optimize updating attributes and support dangerouslySetInnerHTML
This commit is contained in:
parent
c2d76a9990
commit
528137daff
@ -3,7 +3,6 @@ import React, {
|
|||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useLayoutEffect,
|
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
@ -168,9 +167,9 @@ type DispatchProps = Pick<GlobalActions, 'toggleMessageSelection' | 'clickInline
|
|||||||
const NBSP = '\u00A0';
|
const NBSP = '\u00A0';
|
||||||
const GROUP_MESSAGE_HOVER_ATTRIBUTE = 'data-is-document-group-hover';
|
const GROUP_MESSAGE_HOVER_ATTRIBUTE = 'data-is-document-group-hover';
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
const APPENDIX_OWN = '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#000" filter="url(#a)"/><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#EEFFDE" class="corner"/></g></svg>';
|
const APPENDIX_OWN = { __html: '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#000" filter="url(#a)"/><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 016 17z" fill="#EEFFDE" class="corner"/></g></svg>' };
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
const APPENDIX_NOT_OWN = '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#000" filter="url(#a)"/><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#FFF" class="corner"/></g></svg>';
|
const APPENDIX_NOT_OWN = { __html: '<svg width="9" height="20" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-50%" y="-14.7%" width="200%" height="141.2%" filterUnits="objectBoundingBox" id="a"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0621962482 0 0 0 0 0.138574144 0 0 0 0 0.185037364 0 0 0 0.15 0" in="shadowBlurOuter1"/></filter></defs><g fill="none" fill-rule="evenodd"><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#000" filter="url(#a)"/><path d="M3 17h6V0c-.193 2.84-.876 5.767-2.05 8.782-.904 2.325-2.446 4.485-4.625 6.48A1 1 0 003 17z" fill="#FFF" class="corner"/></g></svg>' };
|
||||||
const APPEARANCE_DELAY = 10;
|
const APPEARANCE_DELAY = 10;
|
||||||
const NO_MEDIA_CORNERS_THRESHOLD = 18;
|
const NO_MEDIA_CORNERS_THRESHOLD = 18;
|
||||||
|
|
||||||
@ -233,8 +232,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const bottomMarkerRef = useRef<HTMLDivElement>(null);
|
const bottomMarkerRef = useRef<HTMLDivElement>(null);
|
||||||
// eslint-disable-next-line no-null/no-null
|
|
||||||
const appendixRef = useRef<HTMLDivElement>(null);
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useOnIntersect(bottomMarkerRef, observeIntersectionForBottom);
|
useOnIntersect(bottomMarkerRef, observeIntersectionForBottom);
|
||||||
@ -405,13 +403,6 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
message.id,
|
message.id,
|
||||||
);
|
);
|
||||||
useFocusMessage(ref, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer);
|
useFocusMessage(ref, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer);
|
||||||
useLayoutEffect(() => {
|
|
||||||
if (!appendixRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendixRef.current.innerHTML = isOwn ? APPENDIX_OWN : APPENDIX_NOT_OWN;
|
|
||||||
}, [isOwn, withAppendix]);
|
|
||||||
|
|
||||||
let style = '';
|
let style = '';
|
||||||
let calculatedWidth;
|
let calculatedWidth;
|
||||||
@ -766,7 +757,9 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
</Button>
|
</Button>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
{withCommentButton && <CommentButton message={message} disabled={noComments} />}
|
{withCommentButton && <CommentButton message={message} disabled={noComments} />}
|
||||||
{withAppendix && <div className="svg-appendix" ref={appendixRef} />}
|
{withAppendix && (
|
||||||
|
<div className="svg-appendix" dangerouslySetInnerHTML={isOwn ? APPENDIX_OWN : APPENDIX_NOT_OWN} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{message.inlineButtons && (
|
{message.inlineButtons && (
|
||||||
<InlineButtons message={message} onClick={clickInlineButton} />
|
<InlineButtons message={message} onClick={clickInlineButton} />
|
||||||
|
|||||||
@ -223,7 +223,9 @@ function createNode($element: VirtualElement): Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(props).forEach((key) => {
|
Object.keys(props).forEach((key) => {
|
||||||
addAttribute(element, key, props[key]);
|
if (props[key] !== undefined) {
|
||||||
|
setAttribute(element, key, props[key]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$element.children = children.map(($child, i) => (
|
$element.children = children.map(($child, i) => (
|
||||||
@ -395,31 +397,31 @@ function updateAttributes($current: VirtualRealElement, $new: VirtualRealElement
|
|||||||
const newKeys = Object.keys($new.props);
|
const newKeys = Object.keys($new.props);
|
||||||
|
|
||||||
currentKeys.forEach((key) => {
|
currentKeys.forEach((key) => {
|
||||||
if ($current.props[key] !== undefined && $new.props[key] === undefined) {
|
const currentValue = $current.props[key];
|
||||||
removeAttribute(element, key, $current.props[key]);
|
const newValue = $new.props[key];
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentValue !== undefined
|
||||||
|
&& (
|
||||||
|
newValue === undefined
|
||||||
|
|| (currentValue !== newValue && key.startsWith('on'))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
removeAttribute(element, key, currentValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
newKeys.forEach((key) => {
|
newKeys.forEach((key) => {
|
||||||
if ($new.props[key] === undefined) {
|
const currentValue = $current.props[key];
|
||||||
return;
|
const newValue = $new.props[key];
|
||||||
}
|
|
||||||
|
|
||||||
if ($current.props[key] !== $new.props[key]) {
|
if (newValue !== undefined && newValue !== currentValue) {
|
||||||
if ($current.props[key] === undefined) {
|
setAttribute(element, key, newValue);
|
||||||
addAttribute(element, key, $new.props[key]);
|
|
||||||
} else {
|
|
||||||
updateAttribute(element, key, $current.props[key], $new.props[key]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAttribute(element: HTMLElement, key: string, value: any) {
|
function setAttribute(element: HTMLElement, key: string, value: any) {
|
||||||
if (value === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// An optimization attempt
|
// An optimization attempt
|
||||||
if (key === 'className') {
|
if (key === 'className') {
|
||||||
element.className = value;
|
element.className = value;
|
||||||
@ -428,6 +430,9 @@ function addAttribute(element: HTMLElement, key: string, value: any) {
|
|||||||
(element as HTMLInputElement).value = value;
|
(element as HTMLInputElement).value = value;
|
||||||
} else if (key === 'style') {
|
} else if (key === 'style') {
|
||||||
element.style.cssText = value;
|
element.style.cssText = value;
|
||||||
|
} else if (key === 'dangerouslySetInnerHTML') {
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
element.innerHTML = value.__html;
|
||||||
} else if (key.startsWith('on')) {
|
} else if (key.startsWith('on')) {
|
||||||
addEventListener(element, key, value, key.endsWith('Capture'));
|
addEventListener(element, key, value, key.endsWith('Capture'));
|
||||||
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
||||||
@ -444,6 +449,8 @@ function removeAttribute(element: HTMLElement, key: string, value: any) {
|
|||||||
(element as HTMLInputElement).value = '';
|
(element as HTMLInputElement).value = '';
|
||||||
} else if (key === 'style') {
|
} else if (key === 'style') {
|
||||||
element.style.cssText = '';
|
element.style.cssText = '';
|
||||||
|
} else if (key === 'dangerouslySetInnerHTML') {
|
||||||
|
element.innerHTML = '';
|
||||||
} else if (key.startsWith('on')) {
|
} else if (key.startsWith('on')) {
|
||||||
removeEventListener(element, key, value, key.endsWith('Capture'));
|
removeEventListener(element, key, value, key.endsWith('Capture'));
|
||||||
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
||||||
@ -453,16 +460,6 @@ function removeAttribute(element: HTMLElement, key: string, value: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAttribute(element: HTMLElement, key: string, oldValue: any, newValue: any) {
|
|
||||||
if (key === 'value') {
|
|
||||||
// Removing and adding value causes a cursor jump
|
|
||||||
(element as HTMLInputElement).value = newValue !== undefined ? newValue : '';
|
|
||||||
} else {
|
|
||||||
removeAttribute(element, key, oldValue);
|
|
||||||
addAttribute(element, key, newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
function DEBUG_addToVirtualTreeSize($current: VirtualRealElement | VirtualDomHead) {
|
function DEBUG_addToVirtualTreeSize($current: VirtualRealElement | VirtualDomHead) {
|
||||||
DEBUG_virtualTreeSize += $current.children.length;
|
DEBUG_virtualTreeSize += $current.children.length;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user