perf(core): avoid intermediate arrays in definition (#61445)

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
This commit is contained in:
Kristiyan Kostadinov
2025-05-17 13:22:03 +02:00
committed by Jessica Janiuk
parent cf8c853523
commit b785256b9e
9 changed files with 21 additions and 35 deletions
+17 -24
View File
@@ -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<T>(
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<any>): DirectiveDef<any> | Compon
return getComponentDef(type) || getDirectiveDef(type);
}
function nonNull<T>(value: T | null): value is T {
return value !== null;
}
/**
* @codeGenApi
*/
@@ -657,28 +651,27 @@ function initFeatures<T>(definition: DirectiveDef<T> | ComponentDef<T>): void {
definition.features?.forEach((fn) => fn(definition));
}
export function extractDefListOrFactory(
export function extractDefListOrFactory<T>(
dependencies: TypeOrFactory<DependencyTypeList> | undefined,
pipeDef: false,
): DirectiveDefListOrFactory | null;
export function extractDefListOrFactory(
dependencies: TypeOrFactory<DependencyTypeList> | undefined,
pipeDef: true,
): PipeDefListOrFactory | null;
export function extractDefListOrFactory(
dependencies: TypeOrFactory<DependencyTypeList> | undefined,
pipeDef: boolean,
): unknown {
defExtractor: (type: Type<unknown>) => 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;
};
}
/**
+4 -4
View File
@@ -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<any>[] | (() => Type<any>[]),
): void {
const def = type.ɵcmp as ComponentDef<any>;
def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */ false);
def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */ true);
def.directiveDefs = extractDefListOrFactory(directives, extractDirectiveDef);
def.pipeDefs = extractDefListOrFactory(pipes, getPipeDef);
}
/**
@@ -410,7 +410,6 @@
"nextNgElementId",
"ngOnChangesSetInput",
"ngZoneInstanceId",
"nonNull",
"noop",
"noop2",
"normalizeAnimationEntry",
@@ -420,7 +420,6 @@
"nextNgElementId",
"ngOnChangesSetInput",
"ngZoneInstanceId",
"nonNull",
"noop",
"noop2",
"notFoundValueOrThrow",
@@ -527,7 +527,6 @@
"ngOnChangesSetInput",
"ngZoneInstanceId",
"noSideEffects",
"nonNull",
"noop",
"noop2",
"normalizeValidators",
@@ -517,7 +517,6 @@
"ngOnChangesSetInput",
"ngZoneInstanceId",
"noSideEffects",
"nonNull",
"noop",
"noop2",
"normalizeValidators",
@@ -378,7 +378,6 @@
"nextNgElementId",
"ngOnChangesSetInput",
"ngZoneInstanceId",
"nonNull",
"noop",
"noop2",
"notFoundValueOrThrow",
@@ -608,7 +608,6 @@
"noMatch2",
"noSideEffects",
"nodeChildrenAsMap",
"nonNull",
"noop",
"noop2",
"normalizeQueryParams",
@@ -316,7 +316,6 @@
"nextNgElementId",
"ngOnChangesSetInput",
"ngZoneInstanceId",
"nonNull",
"noop",
"noop2",
"notFoundValueOrThrow",