perf(ivy): avoid unnecessary DOM reads in styling instructions (#32716)

Before this refactoring native node `classList` / `style` properties were
read even if not used. The reason for this was desire to avoid code duplication
between procedural and non-procedural renderers. Unfortunatelly for the case
which will be used by most users (a procedura renderer) the `classList` / `style`
properties were read twice, making the `setStyle` \ `setClass` functions the
most expensive ones (self time) in several benchmarks (large table, expanding
rows).

This refactoring adds a bit of code duplication in order to get better
runtime performance. The code duplication will be removed when we drop
checks for a procedural renderer.

PR Close #32716
This commit is contained in:
Pawel Kozlowski 2019-09-17 14:39:36 +02:00 committed by Andrew Kushnir
parent 3ace25f4a1
commit 05e1b3b312

View File

@ -712,25 +712,37 @@ export function setStylingMapsSyncFn(fn: SyncStylingMapsFn) {
*/ */
export const setStyle: ApplyStylingFn = export const setStyle: ApplyStylingFn =
(renderer: Renderer3 | null, native: RElement, prop: string, value: string | null) => { (renderer: Renderer3 | null, native: RElement, prop: string, value: string | null) => {
// the reason why this may be `null` is either because if (renderer !== null) {
// it's a container element or it's a part of a test if (value) {
// environment that doesn't have styling. In either // opacity, z-index and flexbox all have number values
// case it's safe not to apply styling to the element. // and these need to be converted into strings so that
const nativeStyle = native.style; // they can be assigned properly.
if (value) { value = value.toString();
// opacity, z-index and flexbox all have number values ngDevMode && ngDevMode.rendererSetStyle++;
// and these need to be converted into strings so that if (isProceduralRenderer(renderer)) {
// they can be assigned properly. renderer.setStyle(native, prop, value, RendererStyleFlags3.DashCase);
value = value.toString(); } else {
ngDevMode && ngDevMode.rendererSetStyle++; // The reason why native style may be `null` is either because
renderer && isProceduralRenderer(renderer) ? // it's a container element or it's a part of a test
renderer.setStyle(native, prop, value, RendererStyleFlags3.DashCase) : // environment that doesn't have styling. In either
(nativeStyle && nativeStyle.setProperty(prop, value)); // case it's safe not to apply styling to the element.
} else { const nativeStyle = native.style;
ngDevMode && ngDevMode.rendererRemoveStyle++; if (nativeStyle != null) {
renderer && isProceduralRenderer(renderer) ? nativeStyle.setProperty(prop, value);
renderer.removeStyle(native, prop, RendererStyleFlags3.DashCase) : }
(nativeStyle && nativeStyle.removeProperty(prop)); }
} else {
ngDevMode && ngDevMode.rendererRemoveStyle++;
if (isProceduralRenderer(renderer)) {
renderer.removeStyle(native, prop, RendererStyleFlags3.DashCase);
} else {
const nativeStyle = native.style;
if (nativeStyle != null) {
nativeStyle.removeProperty(prop);
}
}
}
} }
}; };
@ -739,20 +751,31 @@ export const setStyle: ApplyStylingFn =
*/ */
export const setClass: ApplyStylingFn = export const setClass: ApplyStylingFn =
(renderer: Renderer3 | null, native: RElement, className: string, value: any) => { (renderer: Renderer3 | null, native: RElement, className: string, value: any) => {
if (className !== '') { if (renderer !== null && className !== '') {
// the reason why this may be `null` is either because
// it's a container element or it's a part of a test
// environment that doesn't have styling. In either
// case it's safe not to apply styling to the element.
const classList = native.classList;
if (value) { if (value) {
ngDevMode && ngDevMode.rendererAddClass++; ngDevMode && ngDevMode.rendererAddClass++;
renderer && isProceduralRenderer(renderer) ? renderer.addClass(native, className) : if (isProceduralRenderer(renderer)) {
(classList && classList.add(className)); renderer.addClass(native, className);
} else {
// the reason why classList may be `null` is either because
// it's a container element or it's a part of a test
// environment that doesn't have styling. In either
// case it's safe not to apply styling to the element.
const classList = native.classList;
if (classList != null) {
classList.add(className);
}
}
} else { } else {
ngDevMode && ngDevMode.rendererRemoveClass++; ngDevMode && ngDevMode.rendererRemoveClass++;
renderer && isProceduralRenderer(renderer) ? renderer.removeClass(native, className) : if (isProceduralRenderer(renderer)) {
(classList && classList.remove(className)); renderer.removeClass(native, className);
} else {
const classList = native.classList;
if (classList != null) {
classList.remove(className);
}
}
} }
} }
}; };