From b785256b9e7f83c9f05fb1afd561f0af49a42e9d Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 17 May 2025 13:22:03 +0200 Subject: [PATCH] perf(core): avoid intermediate arrays in definition (#61445) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A minor performance improvement for `ɵɵdefineComponent` where the underlying `extractDefListOrFactory` call had a chain of `.map.filter` which meant that we were unnecessarily creating intermediate arrays just to filter out the null values. These changes switch to simple `for` loop to get around it. PR Close #61445 --- packages/core/src/render3/definition.ts | 41 ++++++++----------- packages/core/src/render3/scope.ts | 8 ++-- .../bundle.golden_symbols.json | 1 - .../bundling/defer/bundle.golden_symbols.json | 1 - .../forms_reactive/bundle.golden_symbols.json | 1 - .../bundle.golden_symbols.json | 1 - .../hydration/bundle.golden_symbols.json | 1 - .../router/bundle.golden_symbols.json | 1 - .../bundle.golden_symbols.json | 1 - 9 files changed, 21 insertions(+), 35 deletions(-) diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 2904ad7381b..17c888fa47d 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -28,11 +28,9 @@ import type { DependencyTypeList, DirectiveDef, DirectiveDefFeature, - DirectiveDefListOrFactory, HostBindingsFunction, InputTransformFunction, PipeDef, - PipeDefListOrFactory, TypeOrFactory, ViewQueriesFunction, } from './interfaces/definition'; @@ -382,8 +380,8 @@ export function ɵɵdefineComponent( initFeatures(def); const dependencies = componentDefinition.dependencies; - def.directiveDefs = extractDefListOrFactory(dependencies, /* pipeDef */ false); - def.pipeDefs = extractDefListOrFactory(dependencies, /* pipeDef */ true); + def.directiveDefs = extractDefListOrFactory(dependencies, extractDirectiveDef); + def.pipeDefs = extractDefListOrFactory(dependencies, getPipeDef); def.id = getComponentId(def); return def; @@ -394,10 +392,6 @@ export function extractDirectiveDef(type: Type): DirectiveDef | Compon return getComponentDef(type) || getDirectiveDef(type); } -function nonNull(value: T | null): value is T { - return value !== null; -} - /** * @codeGenApi */ @@ -657,28 +651,27 @@ function initFeatures(definition: DirectiveDef | ComponentDef): void { definition.features?.forEach((fn) => fn(definition)); } -export function extractDefListOrFactory( +export function extractDefListOrFactory( dependencies: TypeOrFactory | undefined, - pipeDef: false, -): DirectiveDefListOrFactory | null; -export function extractDefListOrFactory( - dependencies: TypeOrFactory | undefined, - pipeDef: true, -): PipeDefListOrFactory | null; -export function extractDefListOrFactory( - dependencies: TypeOrFactory | undefined, - pipeDef: boolean, -): unknown { + defExtractor: (type: Type) => T | null, +): (() => T[]) | T[] | null { if (!dependencies) { return null; } - const defExtractor = pipeDef ? getPipeDef : extractDirectiveDef; + return () => { + const resolvedDependencies = typeof dependencies === 'function' ? dependencies() : dependencies; + const result: T[] = []; - return () => - (typeof dependencies === 'function' ? dependencies() : dependencies) - .map((dep) => defExtractor(dep)) - .filter(nonNull); + for (const dep of resolvedDependencies) { + const definition = defExtractor(dep); + if (definition !== null) { + result.push(definition); + } + } + + return result; + }; } /** diff --git a/packages/core/src/render3/scope.ts b/packages/core/src/render3/scope.ts index 5627b95cd39..2d2f41c5958 100644 --- a/packages/core/src/render3/scope.ts +++ b/packages/core/src/render3/scope.ts @@ -11,9 +11,9 @@ import {Type} from '../interface/type'; import {flatten} from '../util/array_utils'; import {noSideEffects} from '../util/closure'; import {EMPTY_ARRAY} from '../util/empty'; -import {getNgModuleDefOrThrow} from './def_getters'; +import {getNgModuleDefOrThrow, getPipeDef} from './def_getters'; -import {extractDefListOrFactory} from './definition'; +import {extractDefListOrFactory, extractDirectiveDef} from './definition'; import {depsTracker} from './deps_tracker/deps_tracker'; import { ComponentDef, @@ -38,8 +38,8 @@ export function ɵɵsetComponentScope( pipes: Type[] | (() => Type[]), ): void { const def = type.ɵcmp as ComponentDef; - def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */ false); - def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */ true); + def.directiveDefs = extractDefListOrFactory(directives, extractDirectiveDef); + def.pipeDefs = extractDefListOrFactory(pipes, getPipeDef); } /** diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index cdcc9abb9af..b483468db21 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -410,7 +410,6 @@ "nextNgElementId", "ngOnChangesSetInput", "ngZoneInstanceId", - "nonNull", "noop", "noop2", "normalizeAnimationEntry", diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index 1376a17cda4..9353cc9bbcc 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -420,7 +420,6 @@ "nextNgElementId", "ngOnChangesSetInput", "ngZoneInstanceId", - "nonNull", "noop", "noop2", "notFoundValueOrThrow", diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index 2f60b0276e8..a219b346257 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -527,7 +527,6 @@ "ngOnChangesSetInput", "ngZoneInstanceId", "noSideEffects", - "nonNull", "noop", "noop2", "normalizeValidators", diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index bcc28e7fee9..229dc61404d 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -517,7 +517,6 @@ "ngOnChangesSetInput", "ngZoneInstanceId", "noSideEffects", - "nonNull", "noop", "noop2", "normalizeValidators", diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index ef7bc0ca386..bfa08f60ab0 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -378,7 +378,6 @@ "nextNgElementId", "ngOnChangesSetInput", "ngZoneInstanceId", - "nonNull", "noop", "noop2", "notFoundValueOrThrow", diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 931bcaeeac1..e8f84d38c74 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -608,7 +608,6 @@ "noMatch2", "noSideEffects", "nodeChildrenAsMap", - "nonNull", "noop", "noop2", "normalizeQueryParams", diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index 35a7917fc3c..8960643869e 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -316,7 +316,6 @@ "nextNgElementId", "ngOnChangesSetInput", "ngZoneInstanceId", - "nonNull", "noop", "noop2", "notFoundValueOrThrow",