From 5607ad8c6242c95849924efaa1b62d4664da8fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Wed, 23 Oct 2019 12:58:20 -0700 Subject: [PATCH] perf(ivy): apply static styles/classes directly to an element's style/className properties (#33364) PR Close #33364 --- .../core/src/render3/instructions/element.ts | 4 +- .../core/src/render3/instructions/shared.ts | 24 ++++++++++-- .../core/src/render3/instructions/styling.ts | 4 +- packages/core/src/render3/styling/bindings.ts | 38 ++++++++++++------- .../core/src/render3/util/styling_utils.ts | 3 +- .../cyclic_import/bundle.golden_symbols.json | 18 +++++++++ .../bundling/todo/bundle.golden_symbols.json | 18 +++++++++ 7 files changed, 85 insertions(+), 24 deletions(-) diff --git a/packages/core/src/render3/instructions/element.ts b/packages/core/src/render3/instructions/element.ts index 114351d6ca..18c99c35ff 100644 --- a/packages/core/src/render3/instructions/element.ts +++ b/packages/core/src/render3/instructions/element.ts @@ -65,7 +65,7 @@ export function ɵɵelementStart( } if ((tNode.flags & TNodeFlags.hasInitialStyling) === TNodeFlags.hasInitialStyling) { - renderInitialStyling(renderer, native, tNode); + renderInitialStyling(renderer, native, tNode, false); } appendChild(native, tNode, lView); @@ -222,7 +222,7 @@ export function ɵɵelementHostAttrs(attrs: TAttributes) { // attribute values to the element. if (stylingNeedsToBeRendered) { const renderer = lView[RENDERER]; - renderInitialStyling(renderer, native, tNode); + renderInitialStyling(renderer, native, tNode, true); } } } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 0d10dccf31..03ddd89663 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -31,10 +31,11 @@ import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIE import {assertNodeOfPossibleTypes} from '../node_assert'; import {isNodeMatchingSelectorList} from '../node_selector_matcher'; import {ActiveElementFlags, enterView, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, leaveView, leaveViewProcessExit, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state'; -import {renderStylingMap} from '../styling/bindings'; +import {renderStylingMap, writeStylingValueDirectly} from '../styling/bindings'; import {NO_CHANGE} from '../tokens'; import {isAnimationProp} from '../util/attrs_utils'; import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils'; +import {getInitialStylingValue} from '../util/styling_utils'; import {getLViewParent} from '../util/view_traversal_utils'; import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils'; @@ -1832,7 +1833,22 @@ export function textBindingInternal(lView: LView, index: number, value: string): * applied once the element is instantiated. This function applies each of the static * style and class entries to the element. */ -export function renderInitialStyling(renderer: Renderer3, native: RElement, tNode: TNode) { - renderStylingMap(renderer, native, tNode.classes, true); - renderStylingMap(renderer, native, tNode.styles, false); +export function renderInitialStyling( + renderer: Renderer3, native: RElement, tNode: TNode, append: boolean) { + if (tNode.classes !== null) { + if (append) { + renderStylingMap(renderer, native, tNode.classes, true); + } else { + const classes = getInitialStylingValue(tNode.classes); + writeStylingValueDirectly(renderer, native, classes, true, null); + } + } + if (tNode.styles !== null) { + if (append) { + renderStylingMap(renderer, native, tNode.styles, false); + } else { + const styles = getInitialStylingValue(tNode.styles); + writeStylingValueDirectly(renderer, native, styles, false, null); + } + } } diff --git a/packages/core/src/render3/instructions/styling.ts b/packages/core/src/render3/instructions/styling.ts index 8526a243ae..8ce466f1f9 100644 --- a/packages/core/src/render3/instructions/styling.ts +++ b/packages/core/src/render3/instructions/styling.ts @@ -441,9 +441,7 @@ function normalizeStylingDirectiveInputValue( if (isClassBased) { value = concatString(initialValue, forceClassesAsString(bindingValue)); } else { - value = concatString( - initialValue, - forceStylesAsString(bindingValue as{[key: string]: any} | null | undefined, true), ';'); + value = concatString(initialValue, forceStylesAsString(bindingValue, true), ';'); } } return value; diff --git a/packages/core/src/render3/styling/bindings.ts b/packages/core/src/render3/styling/bindings.ts index 0ce5ae6084..149d26ad99 100644 --- a/packages/core/src/render3/styling/bindings.ts +++ b/packages/core/src/render3/styling/bindings.ts @@ -700,20 +700,10 @@ export function applyStylingMapDirectly( } if (writeToAttrDirectly) { - let valueToApply: string; - if (isClassBased) { - valueToApply = typeof value === 'string' ? value : objectToClassName(value); - if (initialValue !== null) { - valueToApply = concatString(initialValue, valueToApply, ' '); - } - setClassName(renderer, element, valueToApply); - } else { - valueToApply = forceStylesAsString(value as{[key: string]: any}, true); - if (initialValue !== null) { - valueToApply = initialValue + ';' + valueToApply; - } - setStyleAttr(renderer, element, valueToApply); - } + const initialValue = + hasInitial && !bindingValueContainsInitial ? getInitialStylingValue(context) : null; + const valueToApply = + writeStylingValueDirectly(renderer, element, value, isClassBased, initialValue); setValue(data, cachedValueIndex, valueToApply || null); } else { const applyFn = isClassBased ? setClass : setStyle; @@ -751,6 +741,26 @@ export function applyStylingMapDirectly( } } +export function writeStylingValueDirectly( + renderer: any, element: RElement, value: {[key: string]: any} | string | null, + isClassBased: boolean, initialValue: string | null): string { + let valueToApply: string; + if (isClassBased) { + valueToApply = typeof value === 'string' ? value : objectToClassName(value); + if (initialValue !== null) { + valueToApply = concatString(initialValue, valueToApply, ' '); + } + setClassName(renderer, element, valueToApply); + } else { + valueToApply = forceStylesAsString(value, true); + if (initialValue !== null) { + valueToApply = initialValue + ';' + valueToApply; + } + setStyleAttr(renderer, element, valueToApply); + } + return valueToApply; +} + /** * Applies the provided styling prop/value to the element directly (without context resolution). * diff --git a/packages/core/src/render3/util/styling_utils.ts b/packages/core/src/render3/util/styling_utils.ts index 1d6e3c9bcb..8f8eb65b85 100644 --- a/packages/core/src/render3/util/styling_utils.ts +++ b/packages/core/src/render3/util/styling_utils.ts @@ -296,7 +296,8 @@ export function forceClassesAsString(classes: string | {[key: string]: any} | nu } export function forceStylesAsString( - styles: {[key: string]: any} | null | undefined, hyphenateProps: boolean): string { + styles: {[key: string]: any} | string | null | undefined, hyphenateProps: boolean): string { + if (typeof styles == 'string') return styles; let str = ''; if (styles) { const props = Object.keys(styles); diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 3c63b957fd..1f169c4df8 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -269,6 +269,9 @@ { "name": "findDirectiveMatches" }, + { + "name": "forceStylesAsString" + }, { "name": "generateExpandoInstructionBlock" }, @@ -404,6 +407,9 @@ { "name": "hasTagAndTypeMatch" }, + { + "name": "hyphenate" + }, { "name": "includeViewProviders" }, @@ -527,6 +533,9 @@ { "name": "noSideEffects" }, + { + "name": "objectToClassName" + }, { "name": "postProcessBaseDirective" }, @@ -605,6 +614,9 @@ { "name": "setClass" }, + { + "name": "setClassName" + }, { "name": "setCurrentDirectiveDef" }, @@ -644,6 +656,9 @@ { "name": "setStyle" }, + { + "name": "setStyleAttr" + }, { "name": "setUpAttributes" }, @@ -668,6 +683,9 @@ { "name": "viewAttachedToChangeDetector" }, + { + "name": "writeStylingValueDirectly" + }, { "name": "ɵɵdefineComponent" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 3a2ba04028..a82221c3f8 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -593,6 +593,9 @@ { "name": "flushStyling" }, + { + "name": "forceStylesAsString" + }, { "name": "forwardRef" }, @@ -851,6 +854,9 @@ { "name": "hasValueChanged" }, + { + "name": "hyphenate" + }, { "name": "includeViewProviders" }, @@ -1079,6 +1085,9 @@ { "name": "normalizeBitMaskValue" }, + { + "name": "objectToClassName" + }, { "name": "patchConfig" }, @@ -1208,6 +1217,9 @@ { "name": "setClass" }, + { + "name": "setClassName" + }, { "name": "setCurrentDirectiveDef" }, @@ -1262,6 +1274,9 @@ { "name": "setStyle" }, + { + "name": "setStyleAttr" + }, { "name": "setUpAttributes" }, @@ -1340,6 +1355,9 @@ { "name": "wrapListener" }, + { + "name": "writeStylingValueDirectly" + }, { "name": "ɵɵadvance" },